sql >> Databáze >  >> RDS >> PostgreSQL

SQL:Pokud jde o NOT IN a NOT EQUAL TO, co je efektivnější a proč?

V PostgreSQL je obvykle poměrně malý rozdíl v rozumné délce seznamu, i když IN je koncepčně mnohem čistší. Velmi dlouhé AND ... <> ... seznamy a velmi dlouhé NOT IN oba seznamy fungují strašně, s AND mnohem horší než NOT IN .

V obou případech, pokud jsou dostatečně dlouhé na to, abyste otázku položili, měli byste místo toho provést test vyloučení spojení nebo poddotazu nad seznamem hodnot.

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);

nebo:

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;

(Na moderních verzích Pg oba stejně vytvoří stejný plán dotazů).

Pokud je seznam hodnot dostatečně dlouhý (mnoho desítek tisíc položek), může analýza dotazu začít mít značné náklady. V tomto okamžiku byste měli zvážit vytvoření TEMPORARY tabulka, COPY data, která do ní chcete vyloučit, případně pro ně vytvořit index a poté použít jeden z výše uvedených přístupů na dočasné tabulce namísto CTE.

Demo:

CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;

kde exclude je seznam hodnot, které se mají vynechat.

Poté porovnám následující přístupy na stejných datech se všemi výsledky v milisekundách:

  • NOT IN seznam:3424,596
  • AND ... seznam:80173.823
  • VALUES na základě JOIN vyloučení:20,727
  • VALUES na základě vyloučení poddotazu:20,495
  • Tabulkové JOIN , žádný index na ex-seznamu:25,183
  • Na základě tabulky dílčích dotazů, žádný index na seznamu ex:23,985

... přístup založený na CTE je více než třitisíckrát rychlejší než AND seznam a 130krát rychlejší než NOT IN seznam.

Kód zde:https://gist.github.com/ringerc/5755247 (zachraňte si oči, vy, kteří sledujete tento odkaz).

Pro velikost této datové sady nemělo přidání indexu na seznam vyloučení žádný rozdíl.

Poznámky:

  • IN seznam vygenerován pomocí SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND seznam vygenerovaný pomocí SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • Vyloučení poddotazů a vyloučení tabulek na základě spojení bylo při opakovaných spuštěních téměř stejné.
  • Zkoumání plánu ukazuje, že Pg překládá NOT IN na <> ALL

Takže... můžete vidět, že je toho opravdu obrovské mezera mezi oběma IN a AND seznamy vs provedení správného spojení. Překvapilo mě, jak rychle se to dělá s CTE pomocí VALUES seznam byl ... analýzou VALUES seznam nezabral téměř vůbec čas a fungoval stejně nebo o něco rychleji než tabulkový přístup ve většině testů.

Bylo by hezké, kdyby PostgreSQL dokázal automaticky rozpoznat nesmyslně dlouhé IN klauzule nebo řetězec podobných AND a přejít na chytřejší přístup, jako je hašované spojení nebo jeho implicitní přeměna na uzel CTE. Právě teď neví, jak to udělat.

Viz také:

  • na toto téma napsal tento užitečný blogový příspěvek Magnus Hagander


  1. Oracle ORA-00979 - není výraz GROUP BY

  2. RMAN Seznam příkazů zálohování

  3. Bezpečnostní sítě

  4. SQL Server ROWCOUNT_BIG()