SQL Calculated Field in der SELECT- und der GROUP BY-Klausel

11

Bei der Abfrage meiner MS SQL Server-Datenbanken muss ich häufig ein berechnetes Feld wie dieses erstellen

(CASE WHEN A.type = 'Workover' THEN 'Workover' 
      ELSE (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' 
                 WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' 
                 WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' 
                 ELSE 'Other' 
            END)
END)

und dann muss ich meine Ergebnisse (unter anderem) nach diesem berechneten Feld gruppieren. Daher habe ich die gleiche Berechnung sowohl in der SELECT- als auch in der GROUP BY-Klausel. Führt SQL Server diese Berechnungen tatsächlich zweimal durch oder ist es intelligent genug, um sie nur einmal durchzuführen?

Dr. Drew
quelle

Antworten:

13

Ich habe die gleiche Berechnung sowohl in der SELECT- als auch in der GROUP BY-Klausel. Führt SQL Server diese Berechnungen tatsächlich zweimal durch oder ist es intelligent genug, um sie nur einmal durchzuführen?

Die einfache Antwort lautet, dass SQL Server keine allgemeinen Garantien dafür gibt, wann und wie oft ein skalarer Ausdruck zur Ausführungszeit ausgewertet wird.

Innerhalb des Optimierers und der Ausführungs-Engine gibt es alle möglichen komplizierten (und nicht dokumentierten) Verhaltensweisen hinsichtlich der Platzierung, Ausführung und Zwischenspeicherung von skalaren Ausdrücken. Books Online hat dazu nicht viel zu sagen, aber es heißt :

Skalare Beschreibung berechnen

Dies beschreibt eines der Verhaltensweisen, auf die ich zuvor hingewiesen habe, die verzögerte Ausführung von Ausdrücken. Ich habe in diesem Blog-Beitrag über einige der anderen aktuellen Verhaltensweisen (die sich jederzeit ändern können) geschrieben .

Eine weitere Überlegung ist, dass das vom Abfrageoptimierer verwendete Kostenmodell derzeit nicht viel zur Kostenschätzung für skalare Ausdrücke beiträgt. Ohne einen soliden Kostenrahmen basieren die aktuellen Ergebnisse auf einer breiten Heuristik oder einem reinen Zufall.

Bei sehr einfachen Ausdrücken spielt es wahrscheinlich keine große Rolle, ob der Ausdruck in den meisten Fällen einmal oder mehrmals ausgewertet wird. Ich bin jedoch auf große Abfragen gestoßen, bei denen die Leistung beeinträchtigt wurde, wenn der Ausdruck sehr oft redundant ausgewertet wurde, oder wenn die Auswertung in einem einzelnen Thread erfolgt, in dem die Auswertung in einem parallelen Zweig der Ausführung vorteilhaft gewesen wäre planen.

Zusammenfassend ist das aktuelle Verhalten undefiniert und es gibt nicht viel in Ausführungsplänen, um herauszufinden, was passiert ist (und es ist nicht immer bequem, einen Debugger beizufügen, um das detaillierte Verhalten der Engine zu untersuchen, wie im Blog-Beitrag).

Wenn Sie auf Fälle stoßen, in denen Probleme mit der Skalarbewertung für die Leistung von Bedeutung sind, wenden Sie sich an den Microsoft-Support. Dies ist der beste Weg, um Feedback zu geben, um zukünftige Versionen des Produkts zu verbessern.

Paul White 9
quelle
3

Wie der Kommentar zu Ihrer Frage besagt, lautet die Antwort (zumindest nach meiner Erfahrung) "Ja". SQL Server ist im Allgemeinen intelligent genug, um eine Neuberechnung zu vermeiden. Sie können dies wahrscheinlich überprüfen, indem Sie den Ausführungsplan in SQL Server Management Studio anzeigen. Jedes berechnete Feld wird bezeichnet Exprxxxxx(wobei xxxxx eine Zahl ist). Wenn Sie wissen, wonach Sie suchen müssen, sollten Sie überprüfen können, ob derselbe Ausdruck verwendet wird.

Um die Diskussion zu erweitern, ist Ihre andere ästhetische Option ein allgemeiner Tabellenausdruck :

with [cte] as
(
    select
        (case when a.type = 'workover' then 'workover' else 
        (case when substring(c.category, 2, 1) = 'd' then 'drilling'
              when substring(c.category, 2, 1) = 'c' then 'completion'
              when substring(c.category, 2, 1) = 'w' then 'workover'
              else 'other' end)
         end)) as [group_key],
         *
    from
        [some_table]
)
select
    [group_key],
    count(*) as [count]
from
    [cte]
group by
    [group_key]

Kurze Antwort: Sie sind funktional identisch mit einer Ansicht, gelten jedoch nur für die Verwendung in der nächsten Anweisung. Ich sehe sie meist als besser lesbare Alternative zu abgeleiteten Tabellen, da sie das Verschachteln vermeiden.

Obwohl sie für diese Frage nicht relevant sind, können sie sich selbst referenzieren und auf diese Weise zur Erstellung rekursiver Abfragen verwendet werden.

Schnell Joe Smith
quelle
@ Quick Joe Smith: Ich würde denken, Sie haben Recht mit dem Exprxxxxx, da ich das auch gesehen habe. Wenn ich dem Ausdruck jedoch manuell (case ... end) einen Namen als OpType gebe und dann das Feld OpType in der GROUP BY-Klausel verwende, wird die Fehlermeldung angezeigt, dass es sich um einen ungültigen Spaltennamen handelt.
Dr. Drew
Leider besteht Ihre einzige Möglichkeit, den Ausdruck zweimal anzugeben, häufig darin, eine der oben genannten Methoden zu verwenden: einen CTE, eine Ansicht oder eine verschachtelte Abfrage.
Quick Joe Smith
2
Es sei denn, Sie wissen auch über CROSS APPLY Bescheid .
Andriy M
Die Verwendung cross applyin diesem Fall ist etwas langwierig und würde sehr wahrscheinlich die Leistung beeinträchtigen, wenn eine unnötige Selbstverbindung eingeführt wird.
Schnell Joe Smith
2
Ich glaube nicht, dass Sie den Vorschlag "verstanden" haben. Das CROSS APPLYdefiniert nur den Alias ​​aus Spalten in derselben Zeile. Keine Notwendigkeit für einen Join. zBSELECT COUNT(*), hilo FROM master..spt_values CROSS APPLY (VALUES(high + low)) V(hilo) GROUP BY hilo
Martin Smith
1

Leistung ist nur ein Aspekt. Das andere ist die Wartbarkeit.

Persönlich neige ich dazu, Folgendes zu tun:

SELECT T.GroupingKey, SUM(T.value)
FROM
(
    SELECT 
        A.*
        (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
        (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
        END) AS GroupingKey
    FROM Table AS A
) AS T

GROUP BY T.GroupingKey

AKTUALISIEREN:

Wenn Sie keine Verschachtelung durchführen möchten, können Sie für jede Tabelle, in der Sie komplexe Ausdrücke verwenden müssen, eine ANSICHT erstellen.

CREATE VIEW TableExtended
AS 
SELECT 
    A.*
    (CASE WHEN A.type = 'Workover' THEN 'Workover' ELSE 
    (CASE WHEN substring(C.category, 2, 1) = 'D' THEN 'Drilling' WHEN substring(C.category, 2, 1) = 'C' THEN 'Completion' WHEN substring(C.category, 2, 1) = 'W' THEN 'Workover' ELSE 'Other' END)
    END) AS GroupingKey
FROM Table AS A

Dann können Sie auswählen, ohne zusätzliche Verschachtelung vorzunehmen.

SELECT GroupingKey, SUM(value)
FROM TableExtended
GROUP BY GroupingKey
Kaspars Ozole
quelle