Ist SQL Server zu bewerten erlaubt A <> B
wie A < B OR A > B
, auch wenn einer der Ausdrücke ist nicht deterministisch ?
Dies ist ein etwas kontroverser Punkt, und die Antwort ist ein qualifiziertes "Ja".
Die beste Diskussion, die mir bekannt ist, wurde als Antwort auf Itzik Ben-Gans Connect-Fehlerbericht Bug with NEWID and Table Expressions gegeben , der geschlossen wurde, da er nicht behoben werden konnte. Connect wurde inzwischen eingestellt, sodass der Link zu einem Webarchiv besteht. Leider ging durch den Niedergang von Connect viel nützliches Material verloren (oder war schwerer zu finden). Wie auch immer, die nützlichsten Zitate von Jim Hogg von Microsoft sind:
Dies trifft den Kern des Problems: Darf die Optimierung die Semantik eines Programms ändern? Dh: Wenn ein Programm bestimmte Antworten liefert, aber langsam ausgeführt wird, ist es dann legitim, dass ein Abfrageoptimierer dieses Programm schneller ausführt und gleichzeitig die angegebenen Ergebnisse ändert?
Vor dem Schreien "NEIN!" (Auch meine persönliche Neigung :-), bedenke: Die gute Nachricht ist, dass in 99% der Fälle die Antworten die gleichen sind. Die Abfrageoptimierung ist also ein klarer Gewinn. Die schlechte Nachricht ist, dass, wenn die Abfrage nebenwirkenden Code enthält, unterschiedliche Pläne tatsächlich unterschiedliche Ergebnisse liefern können. Und NEWID () ist eine solche nebenwirkende (nicht deterministische) "Funktion", die den Unterschied aufdeckt. [Wenn Sie experimentieren, können Sie sich andere ausdenken - zum Beispiel die Kurzschlussbewertung von AND-Klauseln: Lassen Sie die zweite Klausel eine arithmetische Division durch Null auslösen. Verschiedene Optimierungen können diese zweite Klausel VOR der ersten Klausel ausführen.] Dies spiegelt sich wider Craigs Erklärung an anderer Stelle in diesem Thread, dass SqlServer nicht garantiert, wenn skalare Operatoren ausgeführt werden.
Wir haben also die Wahl: Wenn wir ein bestimmtes Verhalten bei Vorhandensein von nicht deterministischem (nebenwirkendem) Code garantieren möchten, sodass die Ergebnisse von JOINs beispielsweise der Semantik einer Ausführung mit verschachtelten Schleifen folgen, dann wir kann geeignete OPTIONEN verwenden, um dieses Verhalten zu erzwingen - wie UC betont. Der resultierende Code wird jedoch langsam ausgeführt - das sind die Kosten, die tatsächlich für die Beeinträchtigung des Abfrageoptimierungsprogramms anfallen.
Alles in allem verschieben wir das Abfrageoptimierungsprogramm in Richtung "erwartungsgemäßes" Verhalten für NEWID () - und tauschen die Leistung gegen "erwartungsgemäßes Ergebnis" aus.
Ein Beispiel für die Änderung des Verhaltens in dieser Hinsicht im Laufe der Zeit ist, dass NULLIF mit nicht deterministischen Funktionen wie RAND () nicht korrekt funktioniert . Es gibt auch andere ähnliche Fälle, in denen z. B. COALESCE
eine Unterabfrage verwendet wird, die zu unerwarteten Ergebnissen führen kann und die ebenfalls schrittweise behandelt werden.
Jim fährt fort:
Den Kreis schließen . . . Ich habe diese Frage mit dem Entwicklerteam besprochen. Und schließlich haben wir aus folgenden Gründen beschlossen, das aktuelle Verhalten nicht zu ändern:
1) Der Optimierer garantiert nicht das Timing oder die Anzahl der Ausführungen von Skalarfunktionen. Dies ist ein langjähriger Grundsatz. Dies ist der grundlegende Spielraum, der dem Optimierer genügend Spielraum lässt, um signifikante Verbesserungen bei der Ausführung von Abfrageplänen zu erzielen.
2) Dieses "einmal pro Zeile" -Verhalten ist kein neues Problem, obwohl es nicht allgemein diskutiert wird. Wir haben angefangen, sein Verhalten in der Yukon-Version zu optimieren. Aber es ist ziemlich schwer, genau zu sagen, was es bedeutet! Gilt dies beispielsweise für Zwischenzeilen, die "auf dem Weg" zum Endergebnis berechnet wurden? - In diesem Fall hängt es eindeutig vom gewählten Plan ab. Oder gilt es nur für die Zeilen, die eventuell im fertigen Ergebnis erscheinen werden? - Hier findet eine üble Rekursion statt, da sind Sie sich sicher einig!
3) Wie bereits erwähnt, verwenden wir standardmäßig die Option "Leistung optimieren" - dies ist in 99% der Fälle der Fall. Die 1% der Fälle, in denen sich das Ergebnis ändern könnte, sind relativ leicht zu erkennen - nebenwirkende "Funktionen" wie NEWID - und leicht zu beheben (als Konsequenz wird Perf. Gehandelt). Diese Standardeinstellung zum erneuten Optimieren der Leistung ist seit langem bekannt und wird akzeptiert. (Ja, es ist nicht die Haltung, die Compiler für herkömmliche Programmiersprachen gewählt haben, aber so soll es sein).
Unsere Empfehlungen lauten also:
a) Vermeiden Sie es, sich auf nicht garantierte Timing- und Ausführungszahlsemantiken zu verlassen. b) Vermeiden Sie NEWID () tief in Tabellenausdrücken. c) Verwenden Sie OPTION, um ein bestimmtes Verhalten zu erzwingen (Trading Perf)
Hoffe, diese Erklärung hilft, unsere Gründe für das Schließen dieses Fehlers zu klären, da "nicht behoben".
Interessanterweise AND NOT (s_guid = NEWID())
ergibt sich der gleiche Ausführungsplan
Dies ist eine Folge der Normalisierung, die sehr früh während der Abfragekompilierung auftritt. Beide Ausdrücke werden in genau dieselbe normalisierte Form kompiliert, sodass derselbe Ausführungsplan erstellt wird.