7

Ich habe drei Tabellen:SQL Query Ausführungsverknüpfung ODER Logik?

SmallTable 
    (id int, flag1 bit, flag2 bit) 
JoinTable 
    (SmallTableID int, BigTableID int) 
BigTable 
    (id int, text1 nvarchar(100), otherstuff...) 

SmallTable hat, höchstens ein paar Dutzend Datensätze. BigTable hat ein paar Millionen, und ist eigentlich eine Ansicht, die UNIONS eine Tabelle in dieser Datenbank mit einer Tabelle in einer anderen Datenbank auf dem gleichen Server.

Hier ist die Logik verbinden:

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=1 OR b.text1 <> 'value1') 

Durchschnittliche verbundene Größe ein paar tausend Ergebnisse ist. Alles angezeigt wird indiziert.

Für die meisten SmallTable Aufzeichnungen, flag1 und flag2 sind 1 gesetzt, so gibt es wirklich keine Notwendigkeit, auch den Index Zugriff auf BigTable.text1, aber SQL Server tut es trotzdem, zu einem teueren indizierte Scan und geschachtelte Schleife führt.

Gibt es einen besseren Weg, um SQL Server anzudeuten, dass, wenn flag1 und flag2 beide 1 gesetzt werden, sollte es nicht einmal bei text1 plagen suchen?

Eigentlich, wenn ich die Verknüpfung zu BigTable vollständig in diesen Fällen vermeiden kann (JoinTable verwaltet, so würde dies kein Problem verursachen), würde diese Schlüsselabfrage noch schneller machen.

+0

+1 für interessante Frage. Hoffe, mehr davon selbst zu lernen! – AdaTheDev

+0

Sie haben einen Index-Scan auf 'BigTable' erwähnt, bei dem es sich um eine Ansicht handelt. Ist es eine indizierte Sicht oder wird die Indexsuche für die zugrunde liegenden Tabellen durchgeführt? Könnten Sie bitte den Abfrageplan hier posten? – Quassnoi

Antwort

5

SQL-Boolesche Auswertung macht NICHT garantiert Betreiber Kurzschluss. Ein klares Beispiel für ein Korrektheitsproblem und Laufzeitfehler finden Sie in On SQL Server boolean operator short-circuit.

Auf der anderen Seite zeigt das Beispiel in meinem Link, was funktioniert für SQL Server arbeiten: Bereitstellung eines Zugriffsweg, den SQL verwenden kann. So, wie mit alle SQL-Performance-Probleme und Fragen, ist das eigentliche Problem nicht in der Art, wie der SQL-Text ausgedrückt wird, sondern im Design Ihres Speichers. Ie. Welchen Indizes steht der Abfrageoptimierer zur Verfügung, um Ihre Anfrage zu erfüllen?

+0

Ich stimme zu - das ist kein Kurzzeitproblem per se, aber ein Problem, bei dem die Abfrage-Engine sendet, um den Wert von 'text1' zu überprüfen, obwohl beide Flags auf 1 gesetzt sind. Es ist eine indizierte Operation, aber unnötig * wenn * alle ausgewählten Datensätze in SmallTable diese Flags gesetzt haben. Wie ich an anderer Stelle angemerkt habe, ist es ein Problem, dass der Abfrageoptimierer nicht "intelligent genug" ist, um den Zweig der Arbeit an BigTable.text1 zu vermeiden, wo alle Datensätze in SmallTable die Textvergleiche nicht erfordern. – richardtallent

+1

Leider gibt es keine prozeduralen/bedingten Abfragebaumoperatoren. Mit anderen Worten, es gibt keine Operatoren, die sagen: "Wenn die Bedingung wahr ist, gehen Sie diesen Pfad hinunter, sonst diesen anderen Pfad". Die Abfrage optimizare muss einen Plan erstellen, der alle möglichen Bedingungen * erfüllt, auch solche mit einer sehr geringen Wahrscheinlichkeit *. Der Abfrageplan kann viele Tricks ausführen, wenn * deterministische * Bedingungen vorliegen, z. Zwei Tabellen in einer Verknüpfung mit einer vertrauenswürdigen Fremdbeziehung * können * eine Tabelle vollständig aus dem Plan entfernen. Hier kommen alle T-SQL-Tricks zum Einsatz, zum Beispiel UNION statt OR. –

+0

Das ist ein sehr guter Kommentar. Ich hatte Mühe, mein Verständnis zu erklären, aber diese IMHO ist eine großartige Erklärung. – AdaTheDev

0

Keine Ahnung, ob dies schneller ohne Testdaten werden ... aber es klingt wie könnte es

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1) AND (s.flag2=1) 
UNION ALL 
SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=0 AND b.text1 <> 'value1') 

Bitte lassen Sie mich wissen, was

auch

geschieht, könnten Sie in der Lage sein, dies zu beschleunigen indem Sie einfach nur eine eindeutige ID für diese Abfrage zurückgeben und dann das Ergebnis verwenden, um den Rest der Daten zu erhalten.

bearbeiten

so etwas?

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1=1) AND (s.flag2=1) 
UNION ALL 
SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE EXISTS 
    (SELECT 1 from BigTable b 
    WHERE 
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%') 
    AND (s.flag2=0 AND b.text1 <> 'value1') 
) 
+0

Versucht dies, es war tatsächlich viel langsamer, wenn eines der Flags auf 1 gesetzt wurde. – richardtallent

+0

Ich denke, wir könnten einen Weg finden, dies mit zusätzlichen Joins und einem Befehl zu tun, aber mein Gehirn ist zu verschwommen, um jetzt daran zu denken. – Hogan

0

Es ist nicht elegant, aber es sollte funktionieren ...

SELECT * FROM 
    SmallTable s 
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
    INNER JOIN BigTable b ON b.ID = j.BigTableID 
WHERE 
    (s.flag1 = 1 and s.flag2 = 1) OR 
    (
     (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%') 
     AND (s.flag2=1 OR b.text1 <> 'value1') 
    ) 
+0

Danke ... versuchte dies, aber die Ausführung war identisch mit dem, was ich sagen konnte. – richardtallent

1

Ich glaube nicht, SQL-Server Kurzschlussbedingungen wie das leider.

SO würde ich vorschlagen, tun 2 Abfragen und UNION sie zusammen. Erste Abfrage mit s.flag1 = 1 und s.flag2 = 1 WHERE Bedingungen, und die zweite Abfrage, die die Verknüpfung zu BigTable mit der s.flag1 <> 1 a s.flag2 <> 1 Bedingungen macht.

This Artikel zu diesem Thema ist lesenswert und enthält in der unteren Zeile:

... SQL Server nicht macht Kurzschließen wie es in anderen Programmiersprachen erfolgt und es nichts, was Sie tun können, um es zu erzwingen .

Update:
This Artikel ist auch eine interessante Lektüre und enthält einige gute Links zu diesem Thema, darunter ein Technet-Chat mit dem Entwicklungsleiter für den SQL Server Query Processor-Team, das kurz erwähnt, dass der Optimierer tut ermöglichen eine Kurzschlussauswertung. Der Gesamteindruck, den ich aus verschiedenen Artikeln erhalte, lautet: "Ja, der Optimierer kann die Möglichkeit eines Kurzschlusses erkennen, aber Sie sollten sich nicht darauf verlassen, und Sie können es nicht erzwingen". Daher halte ich den Ansatz der UNION für die beste Lösung. Wenn es keinen Plan gibt, der eine Gelegenheit zum Shortcut ausnutzt, liegt das an dem kostenbasierten Optimierer, der denkt, dass er einen vernünftigen Plan gefunden hat, der es nicht tut (dies wäre auf Indizes, Statistiken usw. zurückzuführen). .

+1

Guter Kommentar, aber während SQL Server die Ausdrucksauswertung nicht abkürzt, führt er * eine Abfrageoptimierung durch. Also, wenn meine Abfrage auf eine Ergebnismenge von SmallTable beschränkt ist, wo beide Flags "1" sind, sollte es intelligent genug sein, die Überprüfung von "BigTable.text1" zu überspringen, aber das würde erfordern, den Ausführungsplan basierend auf den Daten zu ändern. Ich denke, das ist das Kernproblem. – richardtallent

+0

Ja, ich sehe, was Sie sagen, aber ich sehe es immer noch als Abkürzung - der Optimierer müsste in der Lage sein, die Möglichkeit eines Kurzschlusses zu sehen. Ich habe in der Vergangenheit selbst einige Tests durchgeführt, und das habe ich gesehen (keine Optimierung auf diese Weise). – AdaTheDev

+0

Das Problem ist, dass für ein "oder" es in beliebiger Reihenfolge ausgewertet werden kann und es zuerst die rechte Seite tut. – Hogan

0

SQL Server in der Regel greift die Unterabfrage Hinweis (obwohl es kostenlos ist es zu verwerfen):

SELECT  * 
FROM  (
      SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1 
      ) s 
INNER JOIN JoinTable j ON j.SmallTableID = s.ID 
...