In PostgreSQL c'è di solito una differenza abbastanza piccola a lunghezze di lista ragionevoli, anche se IN
è molto più pulito concettualmente. Gli elenchi molto lunghi AND ... <> ...
e gli elenchi molto lunghi NOT IN
eseguono entrambi in modo terribile, con AND
molto peggio di NOT IN
.
In entrambi i casi, se sono abbastanza lunghi da farvi persino porre la domanda, dovreste fare un test di esclusione anti-join o subquery su una lista valori.
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);
o:
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;
(On moderne versioni Pg sia produrrà lo stesso piano di query in ogni caso).
Se l'elenco valori è sufficientemente lungo (molte decine di migliaia di elementi), l'analisi può iniziare a richiedere un costo significativo. A questo punto dovresti prendere in considerazione la creazione di una tabella , COPY
i dati da escludere in essa, possibilmente creando un indice su di essa, quindi utilizzare uno degli approcci precedenti sulla tabella temporanea invece del 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;
dove exclude
è la lista di valori da omettere.
ho confronta i seguenti approcci sugli stessi dati con tutti i risultati in millisecondi:
NOT IN
lista: 3424,596
AND ...
lista: 80173,823
VALUES
basato JOIN
esclusione: 20.727
VALUES
basato subquery esclusione: 20,495
- basato su tabella
JOIN
, nessun indice su ex-list: tavolo 25,183
- sottoquery basato, nessun indice su ex-list: 23,985
... rendendo l'approccio basato su CTE oltre tremila volte più veloce rispetto all'elenco AND
e 130 volte più veloce rispetto all'elenco NOT IN
.
Codice: https://gist.github.com/ringerc/5755247 (proteggiti gli occhi, voi che seguite questo link).
Per questa serie di dati, l'aggiunta di un indice nell'elenco di esclusioni non ha fatto alcuna differenza.
Note:
IN
lista generata con SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
AND
lista generata con SELECT string_agg(item::text, ' AND item <> ') from exclude;
)
- sottoquery e unisciti ad esclusione tabella basata erano più o meno lo stesso attraverso i funzionamenti ripetuti.
- Esame del piano mostra che si traduce Pg
NOT IN
al <> ALL
Quindi ... si può vedere che non c'è un vero e enorme divario tra i due IN
e AND
liste vs facendo una corretta join. Ciò che mi ha sorpreso è stata la rapidità con cui un CTE utilizzava un elenco VALUES
... l'analisi dell'elenco VALUES
non prendeva quasi più tempo, eseguendo lo stesso o leggermente più veloce di l'approccio tabella nella maggior parte dei test.
Sarebbe bello se PostgreSQL in grado di riconoscere automaticamente un assurdamente lungo IN
clausola o la catena di simili AND
condizioni e passare a un approccio più intelligente come fare un hash join o implicitamente trasformandolo in un nodo di CTE. In questo momento non sa come farlo.
Consulta anche:
Poco nitpick: (?)! L'operatore SQL standard per "non equivale a" è '<>' anche se tutti i DBMS sembrano sostenere la non standard '= "altrettanto bene. –
Quando dici "più efficiente", intendi "più veloce"? "Efficiente" può riferirsi a molte altre cose oltre alla semplice velocità di esecuzione. –