2012-04-02 10 views
1

Ich habe eine Abfrage, die wie folgt aussieht:Schlechte Leistung für OR-Bedingung mit mehreren Tabellen

SELECT * 
FROM A 
INNER JOIN B ON A.AId = B.AId 
WHERE A.ADate BETWEEN @Start and @End 
    or B.BDate BETWEEN @Start and @End 

Beide Tabellen A und B sind etwa die gleiche Größe und viele Reihen haben. Der Ausführungsplan zeigt eine Indexsuche an, sieht aber so aus, als ob er den gesamten Index scannt.

Wenn ich die or zu and ändern, dann ist die Abfrage sehr schnell. Ich denke, dies ist aufgrund der Tatsache, dass das Ergebnis der or kann nicht bekannt, ohne einen Tabellen-Scan in beiden Tabellen, um die or zu berechnen. Die and ist leicht in zwei Operationen aufgeteilt.

Ich habe einige Leute gelesen, die angeben, dass es möglich ist, UNION anstelle von or zu verwenden, aber dies würde möglicherweise doppelte Zeilen einführen, falls beide Bedingungen im OR wahr sind.

Welche Lösung gibt es, so dass ich die Größe von Join reduzieren und eine vollständige Verknüpfung beider Tabellen verhindern kann? Ich bin offen für die Umstrukturierung der Abfrage jedoch möglich, um dies zu tun, aber die Logik der Abfrage benötigen (geben Sie mir Elemente, wo entweder das Datum in a entspricht dem Bereich oder das Datum in B entspricht dem Bereich) um gleich zu bleiben.

Antwort

0

Danke für die Antworten, am Ende habe ich für UNION ALL entschieden, und ich gestaltete eine Abfrage auf der Grundlage der Vereinigung von zwei wählt die sich gegenseitig ausschließen, so würden in der keine Duplikate eingeführt werden.

Zuerst alle Zeilen, in denen ADate im Bereich ist, und Zeilen ausschließen, in denen BDate in dem Bereich ist. Dann erhalten Sie alle Zeilen, in denen BDate im Bereich ist. Die Vereinigung dieser zwei Sätze erzeugt logisch den Satz von Reihen, der ADate oder BDate abdeckt, ohne die Mitte doppelt zu zählen (so wird ein UNION ALL keine Duplikate erzeugen). Lassen Sie mich wissen, wenn Sie einen Fehler in dieser Logik sehen, fand ich es hilfreich, an ein Venn-Diagramm zu denken.

Dies machte die Abfrage die besten der angebotenen Optionen (in meinem Fall), und war nicht übermäßig kompliziert, also ging ich damit.

SELECT * 
FROM A 
INNER JOIN B ON A.AId = B.AId 
WHERE A.ADate BETWEEN @Start and @End 
    and B.BDate NOT BETWEEN @Start and @End 

UNION ALL 

SELECT * 
FROM A 
INNER JOIN B ON A.AId = B.AId 
WHERE B.BDate BETWEEN @Start and @End 

Vielleicht könnte dies eine Abfrageoptimierung für den OR Operator in einigen Szenarien, vor allem bei der Abfrage eigene, große Tabellen, es funktioniert mit Datumsbereichen, konnte aber mit anderen Prädikaten arbeitet ich mich vorstellen.

1

Ein UNION führt keine doppelten Zeilen ein. Ein UNION ALL könnte Duplikate einführen.

Siehe http://www.w3schools.com/sql/sql_union.asp

ich mir das vorstellen würde:

SELECT * 
FROM A 
INNER JOIN B ON A.AId = B.AId 
WHERE A.ADate BETWEEN @Start and @End 

UNION 

SELECT * 
FROM A 
INNER JOIN B ON A.AId = B.AId 
WHERE B.BDate BETWEEN @Start and @End 

könnte eine schnellere Abfrage sein.

+0

Danke für die Korrektur über UNION, ich werde es ausprobieren. Ich bin ein bisschen besorgt über die unterschiedliche Art, die es tut, um keine Duplikate sicherzustellen, aber es kann schneller sein als das 'oder' Verhalten, das ich sehe. – Kekoa

+0

Sicher.Wenn es nicht gut abschneidet, sollte Mike Parkhill einen Versuch wagen - das ist vielleicht etwas schneller. –

2

Was Vorfilterung jede Tabelle, bevor Sie Inline-Tabellen mit JOIN:

SELECT A.*, B.* 
FROM (SELECT AId AS Id FROM A WHERE A.ADate BETWEEN @Start and @End 
     UNION 
     SELECT BId AS Id FROM B WHERE B.BDate BETWEEN @Start and @End) AS FilteredIds 
INNER JOIN A ON A.AId = FilteredIds.Id 
INNER JOIN B ON B.BId = FilteredIds.Id 
+0

Würde das die Logik nicht ändern? – Kekoa

+0

@Kekoa Wie hat sich die Logik geändert? –

+0

Ich änderte meine Logik ein bisschen - (Chris meine Vorfilterung handelte mehr wie die UND, die er nicht wollte). Jetzt mache ich auch eine Union, aber nicht die ganze Reihe, bis wir gefiltert sind. Könnte schneller sein als die Vereinigung direkt über alles. –