Wie oft läuft ein CTE?

8

Wenn ich einen Cte wie den folgenden Code habe. Wie oft wird der Tisch abgefragt? Ich hatte den Eindruck, dass es nur einmal aufgerufen und im Speicher gespeichert wurde, aber einige meiner Abfragen, die ich ausgeführt habe, scheinen viel länger zu laufen, als sie sein sollten. Was mich glauben lässt, dass es 3 Mal auf den People Table trifft.

with ctegeneric as (select person from people where person = 'dumb')
Select * from ctegeneric 
UNION ALL
Select * from ctegeneric 
UNION ALL
Select * from ctegeneric 
scripter78
quelle

Antworten:

7

Die Syntax beiseite zu legen war nicht korrekt (vor der Bearbeitung).

NEIN, es wird nicht gespeichert.

Ein gutes Beispiel ist ein Join. Es wird mehrmals in einem Loop-Join aufgerufen. Bei einem teuren CTE, der mehrmals aufgerufen wird, ist es der richtige Weg, sich auf #temp einzulassen.

Paparazzo
quelle
Vielen Dank, dass Sie sich nicht von meinem schnell geschriebenen generischen Beispiel erwischen lassen. Das ist genau die Antwort, die zur Frage gehört. Vielen Dank dafür.
scripter78
4

In SQL kann ein CTE nur in der (one) -Anweisung verwendet / referenziert werden, in der er definiert ist. Und wir wissen, wo Anweisungen enden, indem wir Anweisungsterminatoren ( ;) verwenden.

SQL Server verzeiht und erlaubt Entwicklern, diese Terminatoren nicht zu setzen (außer in besonderen Fällen, in denen es sich beschwert), aber es ist wirklich eine gute Praxis (und Microsoft empfiehlt es), sie nach jeder Anweisung zu verwenden. Wenn Sie sie platziert hätten, wäre es offensichtlich, dass Ihr Code als 3 Anweisungen analysiert wird:

--- 1st statement starts ---
with ctegeneric as (select person from people where person = 'dumb') 
Select * from ctegeneric ;    -- and ends here

--- 2nd statement starts ---
Select * from ctegeneric ;    -- and ends here

--- 3rd statement starts ---
Select * from ctegeneric ;    -- and ends here

Ihre zweite und dritte Anweisung sollten also überhaupt nicht funktionieren und geben den Fehler zurück:

INVALID OBJECT NAME: ctegeneric

Sobald die Aussage, wo der CTE gemacht wird, endet, verlieren Sie die Fähigkeit, ihn erneut zu referenzieren.

Es ist so ähnlich (dies ist auch keine gültige Syntax, nur eine andere Art, über CTEs nachzudenken):

WITH ctegeneric AS (SELECT person 
                    FROM people 
                    WHERE person = 'dumb')

BEGIN
Select * from ctegeneric ;
END

Sie können die drei Auswahlen jedoch mit einem UNION/UNION ALL:

WITH ctegeneric AS (SELECT person 
                    FROM people 
                    WHERE person = 'dumb')   
Select * from ctegeneric
UNION
Select * from ctegeneric
UNION
Select * from ctegeneric ;
MguerraTorres
quelle
Ja, ich habe die Abfrage schnell nur als allgemeines Beispiel geschrieben und sie mit den Gewerkschaften aktualisiert, um das Beispiel zu verdeutlichen. Wird die Personentabelle innerhalb des CTE bei allen drei ausgewählten Optionen 1-mal oder 3-mal aufgerufen?
scripter78
Ich glaube nicht, dass es wie eine temporäre Tabelle oder die Ergebnisse einer Tabellenvariablen gespeichert wird. Es wird jedes Mal aufgerufen, wenn Sie es in einer Abfrage verwenden. Vielen Dank auch für die Bearbeitung Ihrer ursprünglichen Frage. Bitte haben Sie Verständnis dafür, dass ich darauf aufmerksam gemacht habe, da andere sich in Zukunft mit dieser Frage befassen werden und möglicherweise auf das Problem stoßen.
MguerraTorres
4

Der Optimierer nimmt das übermittelte SQL und übersetzt es in eine Reihe von Aktionen, die als Abfrageplan bezeichnet werden. Dabei ist es frei, diese Aktionen in einer beliebigen Reihenfolge anzuordnen, die logisch der SQL entspricht. Dies kann dazu führen, dass auf ein in der SQL erwähntes Objekt einmal, mehrmals oder gar nicht zugegriffen wird. Dies gilt für Objekte im CTE.

Obwohl allgemein beobachtet wird, dass auf Objekte, die im CTE erwähnt werden, nur dann zugegriffen wird, gibt es keine Garantie dafür, dass dies geschieht.

Wie bei jeder anderen SQL-Anweisung gelten die über den CTE-Teil verarbeiteten Daten nur für die Dauer dieser Anweisung. Wenn in einer nachfolgenden Anweisung auf dieselben Daten verwiesen wird, wird erneut darauf zugegriffen.

Michael Green
quelle
3

Dieser Code funktioniert nicht wirklich. (op aktualisiertes Skript, nachdem diese Antwort veröffentlicht wurde)

CTEs sind effektiv Unterabfragen (die die Rekursion unterstützen). Sie können nur im Rahmen dieses bestimmten Befehls referenziert werden. Als solches würde Ihre erste select * -Anweisung funktionieren, die nächsten beiden würden den Fehler machen, den CTE nicht finden zu können. Damit diese funktionieren, müssen Sie für jede Referenz einen anderen CTE erstellen. Und in dieser Situation würden Sie die Personentabelle dreimal treffen, einmal für jede Abfrage.

Um dies zu verbessern, können Sie Ihre Ergebnisse entweder in eine temporäre Tabelle oder in eine Tabellenvariable einfügen und diese dann einfach abfragen.

Nic
quelle