6

「Yからランク付けされたX」データを表示できるようにしたいテーブルがあります。特に、個々の行のデータを比較的効率的な方法で(つまり、テーブル内のすべての行を選択せず​​に)表示できるようにしたいと思います。ランキング自体は非常に単純で、テーブルの1つの列に対する単純なORDERBYです。

Postgresはこの点でいくつかのユニークな課題を提示しているようです。AFAICTには、RANK、ROW_NUMBER、または同等の関数がありません(少なくとも、8.3では、今のところ行き詰まっています)。メーリングリストアーカイブの標準的な答えは、一時的なシーケンスを作成し、そこから選択することのようです。

test=> create temporary sequence tmp_seq;
CREATE SEQUENCE
test=*> select nextval('tmp_seq') as row_number, col1, col2 from foo;

テーブルから1行だけを選択したい場合(ランクではなくPKで選択したい場合)、このソリューションはまだ役に立たないようです。

ランクを非正規化して別の列に格納することで、データの表示を簡単にすることができますが、問題を再配置するだけです。UPDATEはORDERBYをサポートしていないため、ランクを設定するためにUPDATEクエリを作成する方法がわかりません(すべての行を選択し、行ごとに個別のUPDATEを実行する以外は、DBアクティビティが多すぎるようです。ランクの更新が必要になるたびにトリガーします)。

明らかな何かが欠けていますか?これを行う正しい方法は何ですか?

編集:どうやら私は十分に明確ではありませんでした。私はOFFSET/LIMITを知っていますが、それがこの問題の解決にどのように役立つかわかりません。X位のアイテムを選択するのではなく、任意のアイテム(PKなど)を選択して、「312のうち43位」のようなものをユーザーに表示できるようにします。

4

4 に答える 4

7

ランクが必要な場合は、次のようにします

SELECT id,num,rank FROM (
  SELECT id,num,rank() OVER (ORDER BY num) FROM foo
) AS bar WHERE id=4

または、実際に行番号が必要な場合は、

SELECT id,num,row_number FROM (
  SELECT id,num,row_number() OVER (ORDER BY num) FROM foo
) AS bar WHERE id=4

どこかに等しい値がある場合、それらは異なります。必要に応じて、densice_rank()もあります。

もちろん、これにはPostgreSQL8.4が必要です。

于 2009-09-23T18:14:02.570 に答える
5

これだけではありません:

SELECT  *
FROM    mytable
ORDER BY
        col1
OFFSET X LIMIT 1

または私は何かが欠けていますか?

アップデート:

ランクを表示する場合は、次を使用します。

SELECT  mi.*, values[1] AS rank, values[2] AS total
FROM    (
        SELECT  (
                SELECT  ARRAY[SUM(((mi.col1, mi.ctid) < (mo.col1, mo.ctid))::INTEGER), COUNT(*)]
                FROM    mytable mi
                ) AS values
        FROM    mytable mo
        WHERE   mo.id = @myid
        ) q
于 2009-09-23T15:29:47.090 に答える
3

ROW_NUMBERPostgreSQLの機能は。を介して実装されますLIMIT n OFFSET skip

  • ここで概要を見つけてください。
  • ランキングの落とし穴については、このSOの質問を参照してください。

ROW_NUMBER()編集:あなたが単純なランキングの代わりに求めているので:row_number()バージョン8.4でPostgreSQLに導入されました。したがって、更新を検討することをお勧めします。それ以外の場合は、この回避策が役立つ場合があります。

于 2009-09-23T15:29:46.630 に答える
1

以前の返信は、「すべての行を選択してランクを取得する」という質問に取り組んでいますが、これはあなたが望むものではありません...

  • あなたは列を持っています
  • あなたはそのランクを知りたい

ただやってください:

SELECT count(*)FROMtableWHEREスコア>$1

ここで、$ 1は、選択した行のスコアです(選択できるように、表示したいと思います...)。

または行う:

を選択。、(SELECT count()FROM table b WHERE score> b.score)ASランクFROM table AS a WHERE pk=..。

ただし、最後にランク付けされた行を選択した場合は、その前にランク付けされたすべての行をカウントする必要があるため、テーブル全体をスキャンする必要があり、非常に遅くなります。

解決 :

SELECT count(*)FROM(SELECT1FROMテーブルWHEREスコア>$1 LIMIT 30)

あなたは30の最高のスコアの正確なランキングを得るでしょう、そしてそれは速くなります。誰が敗者を気にしますか?

OK、本当に敗者を気にするなら、ヒストグラムを作成する必要があります:

スコアが0から100になる可能性があり、スコアが80未満の敗者が1000000人、スコアが80を超える勝者が10人いるとします。

スコアがXの行の数のヒストグラムを作成します。これは、100行の単純な小さなテーブルです。メインテーブルにトリガーを追加して、ヒストグラムを更新します。

ここで、スコアXの敗者をランク付けする場合、彼のランクはsum(histo)であり、histo_score>Xです。

スコアはおそらく0から100の間ではなく、(たとえば)0から1000000000の間なので、少しファッジする必要があります。たとえば、ヒストグラムのビンを拡大します。したがって、必要なのは最大100個のビンのみであるか、ログヒストグラム分布関数を使用します。

ちなみに、postgresはテーブルをANALYZEするときにこれを行うので、statistics_targetをスコアで100または1000に設定した場合は、ANALYZEを実行してから、次のコマンドを実行します。

EXPLAIN SELECT*FROMテーブルWHEREスコア>$1

あなたは素晴らしい行数の見積もりを得るでしょう。

正確な答えが必要なのは誰ですか?

于 2009-09-26T17:52:03.753 に答える