Za prvé:můžete použít výsledky z CTE vícekrát ve stejném dotazu, to je hlavní funkce CTE .) To, co máte, by fungovalo takto (zatímco stále používáte CTE pouze jednou):
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
Upozornění 1:rank()
rank()
může vrátit více řádků na person_id
s rank = 1
. DISTINCT ON (person_id)
(jako za předpokladu Gordona) je použitelnou náhradou za row_number()
- který pro vás funguje, jak je objasněno další informace. Viz:
Upozornění 2:ORDER BY submission_date DESC
Ani submission_date
ani last_updated
jsou definovány NOT NULL
. Může se jednat o problém s ORDER BY submission_date DESC, last_updated DESC ...
Viz:
Měly by být tyto sloupce skutečně NOT NULL
?
Odpověděli jste:
Pro typ date
nejsou povoleny prázdné řetězce . Ponechte sloupce s možností null. NULL
je správná hodnota pro tyto případy. Použijte NULLS LAST
jak bylo ukázáno, aby se zabránilo NULL
řazení nahoře.
Upozornění 3:OFFSET
Pokud OFFSET
je stejný nebo větší než počet řádků vrácených CTE, získáte žádný řádek , takže také žádný celkový počet. Viz:
Prozatímní řešení
Po vyřešení všech dosavadních výhrad a na základě přidaných informací bychom mohli dospět k tomuto dotazu:
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;
Nyní je CTE ve skutečnosti použit dvakrát. RIGHT JOIN
zaručuje, že získáme celkový počet bez ohledu na OFFSET
. DISTINCT ON
by měl provést OK-ish pouze pro několik řádků na (person_id)
v základním dotazu.
Ale máte široké řady. Jak široký v průměru? Dotaz pravděpodobně povede k sekvenčnímu skenování celé tabulky. Indexy nepomohou (moc). To vše zůstane pro stránkování značně neefektivní . Viz:
Nemůžete zahrnout index pro stránkování, protože je založen na odvozené tabulce z CTE. A vaše skutečná kritéria řazení pro stránkování jsou stále nejasná (ORDER BY id
?). Pokud je cílem stránkování, zoufale potřebujete jiný styl dotazu. Pokud vás zajímá pouze prvních pár stránek, potřebujete jiný styl dotazu. Nejlepší řešení závisí na informacích, které v otázce stále chybí ...
Radikálně rychlejší
Pro váš aktualizovaný cíl:
(Ignorování „pro zadaná kritéria filtru, typ, plán, stav“ pro jednoduchost.)
A:
Na základě těchto dvou specializovaných indexů :
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);
Spusťte tento dotaz:
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;
Každá sada závorek zde je povinná.
Tato úroveň sofistikovanosti by měla získat relativně malou sadu horních řádků radikálně rychleji pomocí daných indexů a bez sekvenčního skenování. Viz:
submission_date
by s největší pravděpodobností měl být typ timestamptz
nebo date
, nikoli - což je v Postgresu v každém případě zvláštní definice typu. Viz:character varying(255)
Mnoho dalších detailů by mohlo být optimalizováno, ale to se vymyká z rukou. Můžete zvážit odborné konzultace.