sql >> Databasteknik >  >> RDS >> PostgreSQL

SQL:När det gäller NOT IN och NOT Equal TO, vilket är mer effektivt och varför?

I PostgreSQL är det vanligtvis en ganska liten skillnad vid rimliga listlängder, även om IN är mycket renare begreppsmässigt. Mycket lång AND ... <> ... listor och mycket långa NOT IN Båda listorna presterar fruktansvärt, med AND mycket värre än NOT IN .

I båda fallen, om de är tillräckligt långa för att du ens ska kunna ställa frågan, bör du istället göra ett anti-join eller subquery exkluderingstest över en värdelista.

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);

eller:

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;

(På moderna Pg-versioner kommer båda att producera samma frågeplan ändå).

Om värdelistan är tillräckligt lång (många tiotusentals artiklar) kan frågeanalys börja ha en betydande kostnad. Vid det här laget bör du överväga att skapa en TEMPORARY tabell, COPY inkludera data för att utesluta i den, eventuellt skapa ett index på den, och sedan använda en av ovanstående metoder på temptabellen istället för 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;

där exclude är listan över värden som ska utelämnas.

Jag jämför sedan följande tillvägagångssätt på samma data med alla resultat i millisekunder:

  • NOT IN lista:3424.596
  • AND ... lista:80173.823
  • VALUES baserad JOIN uteslutning:20,727
  • VALUES baserad uteslutning av underfråga:20,495
  • Tabellbaserad JOIN , inget index på ex-listan:25.183
  • Baserad på delfråga, inget index på ex-listan:23.985

... vilket gör det CTE-baserade tillvägagångssättet över tre tusen gånger snabbare än AND lista och 130 gånger snabbare än NOT IN lista.

Koda här:https://gist.github.com/ringerc/5755247 (skydda ögonen, ni som följer denna länk).

För denna datamängdsstorlek gjorde det ingen skillnad att lägga till ett index på uteslutningslistan.

Anmärkningar:

  • IN lista genererad med SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND lista genererad med SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • Undersökning och kopplingsbaserad tabelluteslutning var i stort sett desamma vid upprepade körningar.
  • Undersökning av planen visar att Pg översätter NOT IN till <> ALL

Så... du kan se att det finns en verkligt stor gap mellan båda IN och AND listor kontra att göra en ordentlig join. Det som förvånade mig var hur snabbt man gör det med en CTE med en VALUES listan ... analyserade VALUES listan tog nästan ingen tid alls, presterade samma eller något snabbare än tabellmetoden i de flesta tester.

Det skulle vara trevligt om PostgreSQL automatiskt kunde känna igen en absurt lång IN sats eller kedja av liknande AND villkor och byt till ett smartare tillvägagångssätt som att göra en hashad join eller implicit omvandla den till en CTE-nod. Just nu vet den inte hur man gör det.

Se även:

  • det här praktiska blogginlägget skrev Magnus Hagander om ämnet


  1. Hur skickar man godtyckliga parametrar till Oracle trigger?

  2. Hur skapar man en databas med flera hyresgäster med delade tabellstrukturer?

  3. Star Trek 3D schackdatamodell

  4. MySQL SQRT() Funktion – Returnera kvadratroten av ett tal i MySQL