sql >> Database teknologi >  >> RDS >> PostgreSQL

SQL:Når det kommer til NOT IN og NOT Equal TO, hvad er mere effektivt og hvorfor?

I PostgreSQL er der normalt en ret lille forskel ved rimelige listelængder, selvom IN er meget renere konceptuelt. Meget lang AND ... <> ... lister og meget lange NOT IN lister fungerer begge forfærdeligt med AND meget værre end NOT IN .

I begge tilfælde, hvis de er lange nok til, at du overhovedet kan stille spørgsmålet, bør du i stedet lave en anti-join- eller underforespørgselsekskluderingstest over en værdiliste.

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å moderne Pg-versioner vil begge producere den samme forespørgselsplan alligevel).

Hvis værdilisten er lang nok (mange titusindvis af elementer), kan forespørgselsparsing begynde at have en betydelig omkostning. På dette tidspunkt bør du overveje at oprette en TEMPORARY tabel, COPY ing af dataene for at udelukke i det, muligvis oprette et indeks på det, og derefter bruge en af ​​ovenstående fremgangsmåder på temp-tabellen i stedet for 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;
 

hvor exclude er listen over værdier, der skal udelades.

Jeg sammenligner derefter følgende tilgange på de samme data med alle resultater i millisekunder:

  • NOT IN liste:3424.596
  • AND ... liste:80173.823
  • VALUES baseret JOIN ekskludering:20.727
  • VALUES baseret ekskludering af underforespørgsler:20.495
  • Tabelbaseret JOIN , intet indeks på tidligere liste:25.183
  • Tabelbaseret underforespørgsel, intet indeks på tidligere liste:23.985

... gør den CTE-baserede tilgang over tre tusinde gange hurtigere end AND liste og 130 gange hurtigere end NOT IN liste.

Kode her:https://gist.github.com/ringerc/5755247 (beskyt dine øjne, I, der følger dette link).

For dette datasæts størrelse gjorde tilføjelse af et indeks på ekskluderingslisten ingen forskel.

Bemærkninger:

  • IN liste genereret med SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND liste genereret med SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • Udelukkelse af underforespørgsler og joinbaserede tabel var stort set det samme på tværs af gentagne kørsler.
  • Undersøgelse af planen viser, at Pg oversætter NOT IN til <> ALL

Så... du kan se, at der er en virkelig enorm mellemrum mellem begge IN og AND lister vs at lave en ordentlig joinforbindelse. Det, der overraskede mig, var, hvor hurtigt jeg gjorde det med en CTE ved hjælp af en VALUES listen var ... ved at analysere VALUES listen tog næsten ingen tid overhovedet, og ydede det samme eller lidt hurtigere end tabeltilgangen i de fleste tests.

Det ville være rart, hvis PostgreSQL automatisk kunne genkende en absurd lang IN klausul eller kæde af lignende AND betingelser og skift til en smartere tilgang som at lave en hashed join eller implicit at omdanne den til en CTE-node. Lige nu ved den ikke, hvordan man gør det.

Se også:

  • dette praktiske blogindlæg skrev Magnus Hagander om emnet


  1. Mountain Lion Postgres kunne ikke oprette forbindelse

  2. Sporing på kolonneniveau og rækkeniveau i fletningsreplikering

  3. Hvordan kan jeg lave en FULD OUTER JOIN i MySQL?

  4. Aktivering af TLS i R12.1