sql >> データベース >  >> RDS >> PostgreSQL

単一のクエリでページ付けされた行と合計数を取得します

    まず最初に:できる 同じクエリでCTEの結果を複数回使用します。これは、の主な機能です。 CTE 。)あなたが持っているものはこのように機能します(CTEを1回だけ使用している間):

    WITH cte AS (
       SELECT * FROM (
          SELECT *, row_number()  -- see below
                    OVER (PARTITION BY person_id
                          ORDER BY submission_date DESC NULLS LAST  -- see below
                                 , last_updated DESC NULLS LAST  -- see below
                                 , id DESC) AS rn
          FROM  tbl
          ) sub
       WHERE  rn = 1
       AND    status IN ('ACCEPTED', 'CORRECTED')
       )
    SELECT *, count(*) OVER () AS total_rows_in_cte
    FROM   cte
    LIMIT  10
    OFFSET 0;  -- see below
    

    警告1:rank()

    rank() person_idごとに複数の行を返すことができます rank = 1の場合 。 DISTINCT ON (person_id) (Gordonが提供したように)row_number()の適切な代替品です -追加情報が明らかになったので、これはあなたのために働きます。参照:

    警告2:ORDER BY submission_date DESC

    どちらのsubmission_date また、last_updated NOT NULLが定義されています 。 ORDER BY submission_date DESC, last_updated DESC ...で問題になる可能性があります 参照:

    それらの列が本当にNOT NULLである必要があります ?

    あなたは答えました:

    タイプdateには空の文字列は使用できません 。列をNULL可能にしてください。 NULL これらの場合の適切な値です。 NULLS LASTを使用します NULLを回避するために示されているように 上に並べ替えられます。

    警告3:OFFSET

    OFFSETの場合 CTEによって返される行数以上の場合、行なしが返されます。 、したがって、合計数もありません。参照:

    暫定ソリューション

    これまでのすべての警告に対処し、追加情報に基づいて、次のクエリに到達する可能性があります:

    WITH cte AS (
       SELECT DISTINCT ON (person_id) *
       FROM   tbl
       WHERE  status IN ('ACCEPTED', 'CORRECTED')
       ORDER  BY person_id, submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC
       )
    SELECT *
    FROM  (
       TABLE  cte
       ORDER  BY person_id  -- ?? see below
       LIMIT  10
       OFFSET 0
       ) sub
    RIGHT  JOIN (SELECT count(*) FROM cte) c(total_rows_in_cte) ON true;
    

    現在、CTEは実際に 2回使用しました。 RIGHT JOIN OFFSETに関係なく、合計数を取得できることを保証します 。 DISTINCT ON (person_id)ごとに数行だけOK-ishを実行する必要があります 基本クエリで。

    しかし あなたは広い列を持っています。平均してどれくらい広いですか?クエリの結果、テーブル全体が順次スキャンされる可能性があります。インデックスは(あまり)役に立ちません。これらはすべてページングには非常に非効率的です 。参照:

    CTEから派生したテーブルに基づいているため、ページングのインデックスを含めることはできません。また、ページングの実際の並べ替え基準はまだ不明です(ORDER BY id ?)。ページングが目標である場合は、別のクエリスタイルがどうしても必要です。最初の数ページだけに関心がある場合は、別のクエリスタイルが必要です。最善の解決策は、質問にまだ欠けている情報に依存します...

    大幅に高速

    更新された目的の場合:

    "指定されたフィルター基準、タイプ、プラン、ステータス"を無視します 簡単にするために。)

    そして:

    これら2つの特殊なインデックスに基づいています :

    CREATE INDEX ON tbl (submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST)
    WHERE  status IN ('ACCEPTED', 'CORRECTED'); -- optional
    
    CREATE INDEX ON tbl (person_id, submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST);
    

    このクエリを実行します:

    WITH RECURSIVE cte AS (
       (
       SELECT t  -- whole row
       FROM   tbl t
       WHERE  status IN ('ACCEPTED', 'CORRECTED')
       AND    NOT EXISTS (SELECT FROM tbl
                          WHERE  person_id = t.person_id 
                          AND   (  submission_date,   last_updated,   id)
                              > (t.submission_date, t.last_updated, t.id)  -- row-wise comparison
                          )
       ORDER  BY submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST
       LIMIT  1
       )
    
       UNION ALL
       SELECT (SELECT t1  -- whole row
               FROM   tbl t1
               WHERE ( t1.submission_date, t1.last_updated, t1.id)
                   < ((t).submission_date,(t).last_updated,(t).id)  -- row-wise comparison
               AND    t1.status IN ('ACCEPTED', 'CORRECTED')
               AND    NOT EXISTS (SELECT FROM tbl
                                  WHERE  person_id = t1.person_id 
                                  AND   (   submission_date,    last_updated,    id)
                                      > (t1.submission_date, t1.last_updated, t1.id)  -- row-wise comparison
                                  )
               ORDER  BY submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST
               LIMIT  1)
       FROM   cte c
       WHERE  (t).id IS NOT NULL
       )
    SELECT (t).*
    FROM   cte
    LIMIT  10
    OFFSET 0;
    

    ここでの括弧のすべてのセットが必要です。

    このレベルの洗練度は、指定されたインデックスを使用し、順次スキャンを行わないことで、比較的少数の最上位行のセットを大幅に高速に取得する必要があります。参照:

    submission_date おそらくタイプtimestamptzである必要があります またはdate character varying(255)ではありません -いずれにせよ、これはPostgresの奇妙な型の定義です。参照:

    さらに多くの詳細が最適化される可能性がありますが、これは手に負えなくなっています。あなたは専門家のコンサルティングを検討するかもしれません。



    1. MySQLの関係

    2. Oracleで結果を垂直方向に表示する

    3. SCUMMダッシュボードを使用したMySQLレプリケーションの効果的な監視:パート2

    4. sun.security.validator.ValidatorException:Java> 1.6で、PKIXパスの構築に失敗しました