まず最初に:できる 同じクエリで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の奇妙な型の定義です。参照:
さらに多くの詳細が最適化される可能性がありますが、これは手に負えなくなっています。あなたは専門家のコンサルティングを検討するかもしれません。