2010-12-28 4 views
1

Wie kann eine Abfrage wie die folgende über 16 Stunden dauern? (Wir haben die Ausführung gestoppt, um nach Optimierungen zu suchen, aber keiner von uns ist DB-Experte.) Es scheint, als sollte es sehr einfach sein, den satzbasierten Ausschluss durchzuführen, richtig?Warum läuft diese Abfrage so viel länger als die Summe der Unterabfragen?

SELECT 
    field 
FROM 
    (subquery that returns 1173126 rows in 20 seconds) 
WHERE 
    field NOT IN (subquery that returns 3927646 rows in 69 seconds) 

Was sollte ich sonst noch in diese Notiz aufnehmen, um Sie mit genügend Informationen zu versorgen?

(Die eigentliche Abfrage folgt, falls etwas tricksy und spezifische Informationen gibt es, dass das Problem verursacht.)

SELECT blob FROM (
     SELECT a.line1 + '|' + substring(a.zip,1,5) as blob 
     FROM registrations r 
     JOIN customers c ON r.custId = c.Id 
     JOIN addresses a ON c.addressId = a.Id 
     WHERE r.purchaseDate > DATEADD(year,-1,getdate()) 
     GROUP BY a.line1 + '|' + substring(a.zip,1,5)) sq 
WHERE blob NOT IN (
     SELECT a.line1 + '|' + substring(a.zip,1,5) as blob 
     FROM registrations r 
     JOIN customers c ON r.custId = c.Id 
     JOIN addresses a ON c.addressId = a.Id 
     WHERE r.purchaseDate BETWEEN DATEADD(year,-5,getdate()) AND DATEADD(year,-1,getdate()) 
     GROUP BY a.line1 + '|' + substring(a.zip,1,5)) 

Antwort

2

Sie scheinen nach den Adressen zu suchen, die innerhalb des letzten Jahres gekauft wurden, aber nicht innerhalb der letzten 5 Jahre.

SELECT DISTINCT a.line1, SUBSTRING(a.zip, 1, 5) 
FROM addresses a 
WHERE id IN 
     (
     SELECT c.addressId 
     FROM customers c 
     JOIN registrations r 
     ON  r.custId = c.id 
     AND  r.purchaseDate > DATEADD(year, -1 ,getdate()) 
     ) 
     AND NOT EXISTS 
     (
     SELECT NULL 
     FROM customers c 
     JOIN registrations r 
     ON  r.custId = c.id 
     JOIN addresses ai 
     ON  ai.id = c.addressId 
     WHERE r.purchaseDate BETWEEN DATEADD(year,-5,getdate()) AND DATEADD(year,-1,getdate()) 
       AND ai.line1 = a.line1 
       AND SUBSTRING(ai.zip, 1, 5) = SUBSTRING(a.zip, 1, 5) 
     ) 

Diese Abfrage kümmert sich die Duplikate von line1, zip auf Adressen mit den verschiedenen IDs. Haben Sie solche Duplikate?

+0

Ja, wir finden "neue" Kunden und es gibt doppelte Zeilen1, Zip-Kombinationen über die IDs. – clweeks

+0

@clweeks: also wenn es vor 3 Jahren einen Kauf von der gleichen Adresse gab, aber mit einer anderen ID, zählt das nicht als neu, oder? – Quassnoi

+0

Korrekt.Jemand anderes im Haushalt könnte den Kauf getätigt haben (oder ihre Daten wurden schlecht abgeglichen) und wir suchen nach Adressen/Haushalten, die neue Einkäufe getätigt haben (wobei "neu" Leute einschließt, die vor über fünf Jahren gekauft haben, aber nicht seither). . – clweeks

2

du nicht erkennen kann, aber eine NICHT IN Aussage wird durch die zu einer IF-Anweisung umgewandelt Suchmaschine abfragen. In diesem Beispiel wird eine riesige IF-Anweisung mit all diesen Zeilen (3.9M) erstellt. Dann muss es jede der IF-Bedingungen bewerten, um zu sehen, ob der Wert existiert. Es ist keine Überraschung, dass es mehr als 16 Stunden dauert.

Sie würden viel besser versuchen, einen Weg zu finden, um dies zu einem EXISTS oder vielleicht einem Join zu konvertieren.

+0

Dies bewertet nicht jede der "IF" -Bedingungen oder sogar sie alle zu bauen. Dies wird Semi-Join genannt und es gibt eine Vielzahl von Algorithmen, um es auszuführen. – Quassnoi

+0

Bevor ich hier gepostet habe, habe ich den Ausführungsplan durchgesehen (den ich nicht wirklich * benutzen kann) und die "Linke Anti-Semi-Verbindung" in der Abfrage gefunden und gedacht, dass das etwas ist, was ich recherchieren musste Aber es gibt so viel zu tun, dass es schwierig ist, Zeit zu finden, sich über alles zu informieren, was ich möchte. – clweeks

+0

@Quassnoi - danke für die Korrektur. Mein Verständnis war, dass eine IF-Anweisung erstellt wird. Führt dies, wenn die Anzahl der Zeilen klein ist, und dann ein Semi-Join für eine größere Anzahl von Zeilen? –

1

Die zweite Unterabfrage wird einmal für jede Zeile in der ersten Unterabfrage ausgeführt.

Das bedeutet, geschätzte Zeit bis zur Fertigstellung wäre um (1.173.126 * 69) = 80.945.394 Sekunden

, die etwa 154 Jahre ist ...

Nachdem Sie hinzugefügt, um die eigentliche Abfrage, das Beste, was für Sie Um die beiden Abfragen zu optimieren, fügen Sie den Tabellen Indizes hinzu. Ich kann Ihnen nicht genau sagen, welche Indizes hinzugefügt werden sollen, aber es gibt viele gute Artikel zur Auswahl der korrekten Indizes für Tabellen.

+0

so funktioniert es nicht. – Quassnoi