SQL: Wenn es um NOT IN und NOT EQUAL TO geht, was ist effizienter und warum?

7

Nehmen wir an, ich habe eine Reihe von Elementen:

  • Artikel 1
  • Item2
  • Item3
  • Item4
  • Item5

Eine Abfrage kann auf zwei Arten erstellt werden. Erstens:

%Vor%

Oder es kann geschrieben werden als:

%Vor%
  1. Was ist effizienter und warum?
  2. An welchem ​​Punkt wird einer effizienter als der andere? Mit anderen Worten, was wäre, wenn es 500 Gegenstände gäbe?

Meine Frage bezieht sich speziell auf PostgreSQL.

    
coderama 11.06.2013, 06:06
quelle

2 Antworten

31

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 zeigen beide eine schreckliche Leistung, wobei AND viel schlechter ist als NOT IN .

Wenn beide in beiden Fällen lang genug sind, dass Sie die Frage überhaupt stellen, sollten Sie stattdessen einen Anti-Join oder Unterabfrage-Ausschluss-Test über eine Werteliste durchführen.

%Vor%

oder:

%Vor%

(Bei modernen Pg-Versionen werden beide den gleichen Abfrageplan erzeugen).

Wenn die Werteliste lang genug ist (viele Zehntausende von Elementen), kann die Parsing-Abfrage mit erheblichen Kosten beginnen. An diesem Punkt sollten Sie in Erwägung ziehen, eine TEMPORARY -Tabelle zu erstellen, COPY in die auszuschließenden Daten aufzunehmen, 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:

%Vor%

Dabei steht exclude für die Liste der Werte, die weggelassen werden sollen.

Ich vergleiche dann die folgenden Ansätze für die gleichen Daten mit allen Ergebnissen in Millisekunden:

  • NOT IN list: 3424.596
  • AND ... list: 80173.823
  • VALUES basierend auf JOIN ausschluss: 20.727
  • VALUES basierter Unterabfrageausschluss: 20.495
  • Tabellenbasiertes JOIN , kein Index für die Ex-Liste: 25.183
  • Unterabfragetabelle basiert, kein Index für Ex-Liste: 23.985

... den CTE-basierten Ansatz über dreitausend Mal schneller als die AND -Liste und 130-mal schneller als die NOT IN -Liste macht.

Code hier: Ссылка (Schild deine Augen, ihr folgt diesem Link).

Für diese Datensatzgröße machte das Hinzufügen eines Indexes zur Ausschlussliste keinen Unterschied.

Anmerkungen:

  • IN Liste erstellt mit SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND list generiert mit SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • Unterabfrage und Join-basierter Tabellenausschluss waren bei wiederholten Läufen sehr ähnlich.
  • Die Überprüfung des Plans zeigt, dass Pg NOT IN in <> ALL übersetzt

Also ... Sie können sehen, dass zwischen den Listen IN und AND eine wirklich große Lücke besteht, anstatt einen richtigen Join zu machen. Was mich überraschte war, wie schnell es mit einem CTE war, das eine VALUES Liste benutzte ... das Parsen der VALUES Liste dauerte fast keine Zeit, sie führte die gleiche oder etwas schneller als die Tabelle aus Ansatz in den meisten Tests.

Es wäre schön, wenn PostgreSQL automatisch eine lächerlich lange IN -Klausel oder eine Kette ähnlicher AND -Konditionen erkennt und zu einem intelligenteren Ansatz wie einem Hash-Join oder einem impliziten Umwandeln in einen CTE-Knoten übergeht. Im Moment weiß es nicht, wie das geht.

Siehe auch:

Craig Ringer 11.06.2013, 06:51
quelle
8

Ich stimme etwas mit der ursprünglich akzeptierten Antwort von @Jayram nicht überein.

Nicht zuletzt ist der Link für SQL Server und widerspricht vielen anderen Artikeln und Antworten. Außerdem gibt es keine Indizes für die Beispieltabelle.

Normalerweise für Unterabfrage-SQL-Konstrukte

  • <> (oder != ) ist ein skalarer Vergleich
  • NOT IN ist ein linksbasierter Anti-Semi-Join relationaler Operator

Einfacher ausgedrückt

  • NOT IN wird zu einer Form von JOIN, die einen Index (außer PostgreSQL!) verwenden kann
  • != ist oft nicht SARGable und ein Index darf nicht verwendet werden

Dies wurde auf dba.se diskutiert: "Die Verwendung von NOT-Logik in Bezug auf Indizes" . Für PostgreSQL, dann diesen erklärten Artikel erklärt das interne mehr (aber nicht für eine Liste von Konstanten mit NOT IN leider).

Wie auch immer, für eine Liste von Konstanten würde ich NOT IN vor <> generell verwenden, weil es einfacher zu lesen ist und aufgrund dessen, was @CraigRinger erklärt hat.

Für eine Unterabfrage ist NOT EXISTS der Weg zu gehen

    
gbn 11.06.2013 07:25
quelle

Tags und Links