In PostgreSQL gibt es normalerweise einen recht kleinen Unterschied bei vernünftigen Listenlängen, obwohl IN
konzeptionell viel sauberer ist. Sehr lange AND ... <> ...
Listen und sehr lange NOT IN
Listen führen beide schrecklich, mit AND
viel schlechter als NOT IN
.
In beiden Fällen, wenn sie lang genug sind, dass Sie sogar die Frage stellen, sollten Sie stattdessen einen Anti-Join oder Unterabfrage-Ausschluss-Test über eine Werteliste durchführen.
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);
oder:
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;
(Auf moderne Pg Versionen beide den gleichen Abfrage-Plan ohnehin produzieren).
Wenn die Werteliste lang genug ist (viele Zehntausende von Elementen), dann kann das Parsen von Abfragen erhebliche Kosten verursachen. An diesem Punkt sollten Sie in Betracht ziehen, eine TEMPORARY
Tabelle zu erstellen, COPY
die auszuschließenden Daten, möglicherweise einen Index dafür zu erstellen, und dann einen der oben genannten Ansätze für die temporäre Tabelle anstelle des CTE zu verwenden.
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;
wo exclude
die Liste der Werte ist zu unterlassen.
Ich vergleiche dann die folgenden Ansätze auf den gleichen Daten mit allen Ergebnissen in Millisekunden:
NOT IN
Liste: 3424,596
AND ...
Liste: 80173,823
VALUES
basiert JOIN
Ausschluss: 20.727
VALUES
basierte Unterabfrage Ausschluss: 20,495
- Tabellenbasierte
JOIN
, kein Index auf ex-Liste: 25,183
- Subquery Tabelle basiert, kein Index auf ex-Liste: 23,985
... die CTE-basierten Ansatz über dreitausend Mal schneller als die AND
Liste und 130-mal schneller als die NOT IN
Liste machen.
Code hier: https://gist.github.com/ringerc/5755247 (Schild deine Augen, ihr folgt diesem Link).
Für diese Datensatzgröße machte das Hinzufügen eines Indexes zur Ausschlussliste keinen Unterschied.
Hinweise:
IN
Liste erstellt mit SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
AND
Liste generiert mit SELECT string_agg(item::text, ' AND item <> ') from exclude;
)
- Subquery und basierte Tabelle Ausschluss verbinden waren sehr ähnlich verläuft über wiederholt.
- Prüfung des Plans zeigt, dass Pg So
NOT IN
zu <> ALL
übersetzt ... können Sie sehen, dass ein wirklich riesige Lücke zwischen den beiden IN
und AND
Listen gibt es eine richtige beitreten vs tun. Was mich überraschte war, wie schnell es mit einem CTE mit einer VALUES
Liste war ... Parsing die VALUES
Liste dauerte fast keine Zeit, Durchführung der gleichen oder etwas schneller als die Tabelle Ansatz in den meisten Tests.
Es wäre schön, wenn PostgreSQL automatisch eine grotesk lange IN
Klausel oder eine Kette von ähnlichen AND
Bedingungen und wie macht einen Hash zu einem intelligenteren Ansatz wechselt erkennen konnte beitreten oder implizit in einen CTE Knoten drehen. Im Moment weiß es nicht, wie das geht. auch
Siehe:
Kleinen nitpick zu gehen: (?)! Der Standard-SQL-Operator für "nicht gleich" ist '<>' obwohl alle DBMS scheinen der Nicht-Standard 'zu unterstützen = 'genauso gut. –
Wenn Sie "effizienter" sagen, meinen Sie "schneller"? "Effizient" kann sich auf viele andere Dinge als nur die Ausführungsgeschwindigkeit beziehen. –