SQL Server: Wofür sind Batching-Anweisungen (dh die Verwendung von „GO“) gut?

76

Ich weiß, dass in SQL Server GO ein Batch-Trennzeichen betrachtet wird .

Meine Frage ist: Was bringt ein Batch-Separator? Welchen Nutzen bringt es Ihnen und warum möchten Sie es nutzen?

Beispiel: Ich habe oft gesehen, dass es im SQL-Code wie folgt verwendet wird, und ich kann nicht verstehen, warum es als Best Practice angesehen wird. Soweit ich das beurteilen kann, wäre der Code ohne alle GOAussagen gleich:

USE AdventureWorks2012;
GO
BEGIN TRANSACTION;
GO
IF @@TRANCOUNT = 0
BEGIN
    SELECT FirstName, MiddleName 
    FROM Person.Person WHERE LastName = 'Adams';
    ROLLBACK TRANSACTION;
    PRINT N'Rolling back the transaction two times would cause an error.';
END;
ROLLBACK TRANSACTION;
PRINT N'Rolled back the transaction.';
GO

(Quelle: Technet-Dokumentation ):

Zain Rizvi
quelle
7
@ JohnnyBones, nein, es ist nicht dasselbe (ich habe in meiner Frage sogar auf diesen Beitrag verlinkt). In diesem Beitrag heißt es im Grunde, dass GO ein Batch-Separator ist, aber meine Frage lautet, wozu ein Batch-Separator gut ist
Zain Rizvi
Schauen Sie sich die Antwort von tvanfosson in dieser Frage an (26 positive Stimmen).
Johnny Bones
3
@ JohnnyBones, danke, dass du auf diese Antwort hingewiesen hast. Es gibt einige gute zusätzliche Hintergrundinformationen, beantwortet aber nicht die Grundfrage, die ich hatte (zum Teufel, eine Person hat sogar einen Kommentar zu dieser Frage hinterlassen und gefragt, wofür Batching-Skripte gut sind)
Zain Rizvi
Auf jeden Fall ein Duplikat davon ... stackoverflow.com/questions/2668529/t-sql-go-statement/…
sam yi

Antworten:

40

In dem dortigen Beispiel nützt es überhaupt nichts.

Viele Anweisungen müssen jedoch die einzigen im Stapel sein.

Wie zum Beispiel CREATE PROCEDURE.

Auch häufig müssen nach dem Vornehmen von Schemaänderungen (z. B. Hinzufügen einer neuen Spalte zu einer vorhandenen Tabelle) Anweisungen, die das neue Schema verwenden, separat in einem anderen Stapel kompiliert werden.

Im Allgemeinen besteht eine Alternative zum Senden separater GOStapel, die durch getrennt sind, darin, die SQL in einem untergeordneten Stapel mit auszuführenEXEC

Martin Smith
quelle
1
Das macht Sinn, danke. Ich habe auch gesehen, dass es in Datenbankschema-Upgrade-Skripten nach jeder einzelnen Operation zum Ändern / Erstellen von Tabellen verwendet wird, selbst wenn die sequentiellen Operationen nicht von der vorherigen abhängig waren. Gibt es einen Grund, dies zu tun, oder wäre das eine Praxis gewesen, um Entwickler nicht dazu zu bringen, über ihre SQL nachzudenken
Zain Rizvi
1
@Zain - Nun, es schadet nicht viel (abgesehen von zunehmenden Netzwerk-Roundtrips) und spart wahrscheinlich einige Fehler, bei denen die Operationen von den vorherigen abhängen.
Martin Smith
Es hört sich so an, als müsste SQL-Code in zwei Stapel aufgeteilt werden, wenn der zweite Stapel mit Objekten arbeitet, die vom ersten Stapel erstellt wurden. Mit anderen Worten, wenn für den zweiten Stapel die SQL-Planungsengine Artefakte verwenden muss, die noch nicht vorhanden sind, würde dies die Planung vermasseln. Ist das korrekt?
Zain Rizvi
7
@Zain - In Bezug auf Tabellen sind Änderungen an vorhandenen Tabellen problematischer. CREATE TABLE T(X INT);SELECT * FROM Tfunktioniert einwandfrei, da die SELECTKompilierung verzögert wird. Aber dann, nachdem die Tabelle erstellt wurde, ALTER TABLE T ADD Y INT;SELECT Y FROM Twürde mit einem ungültigen Spaltennamenfehler fehlschlagen, da diese Anweisung nicht kompiliert kompiliert wird.
Martin Smith
Für mich beantwortet dies die Frage immer noch nicht. "Viele Anweisungen müssen jedoch die einzigen im Stapel sein" - aber was bringt es, einen Stapel zu haben? Wenn der Server eine interne Einschränkung aufweist, die erfordert, dass "viele Anweisungen" die einzigen in einem Stapel sind - Intellisense kann die Anforderung eindeutig erkennen. Warum muss ich dann überhaupt GO schreiben? Warum nicht einfach die Interna so ausführen, wie sie benötigt werden? An Martin - "Zusammenstellung aufgeschoben"? Warum sollte ein SQL-Codierer interne Kompilierungsprozesse verstehen müssen? Die Frage bleibt: Was ist der Sinn der Dosierung?
youcantryreachingme
30

Wie TechNet sagt , GObedeutet dies für die SQL-Dienstprogramme das Ende eines SQL-Stapels. Wenn SQL Server Management Studio beispielsweise auf das Batch-Trennzeichen stößt, weiß es, dass der gesamte bisherige Text eine unabhängige SQL-Abfrage ist.

Wir verwenden eine ähnliche Technik in unserer Software. Wir speichern alle unsere Prozesse, Schemaskripte, Datenkonvertierungen usw. in SQL-Skriptdateien (eingecheckt in die Quellcodeverwaltung). Wenn unser Installationsprogramm eine dieser Skriptdateien liest, teilt GO unserem Parser mit, dass Sie die bereits gelesene SQL ausführen können.

Das Schöne an einem Batch-Trennzeichen GOist, dass Sie zwei SQL-Abfragen in dasselbe Skript aufnehmen können, die normalerweise einen Fehler verursachen würden. Versuchen Sie beispielsweise, dieselbe gespeicherte Prozedur in derselben Skriptdatei zu löschen und neu zu erstellen:

if exists (select * from sys.procedures where name = 'sp_test')
    drop procedure sp_test

create procedure sp_test as
begin
    select 1
end

Wenn Sie den obigen Code ausführen, wird eine Fehlermeldung angezeigt:

Nachricht 156, Ebene 15, Status 1, Prozedur sp_test, Zeile 5 Falsche Syntax in der Nähe des Schlüsselworts 'begin'.

Und SSMS zeigt Ihnen den Fehler:

Falsche Syntax. 'CREATE PROCEDURE' muss die einzige Anweisung in einem Stapel sein.

Die Verwendung eines Batch-Separators kann Ihnen dabei helfen, diesen Fehler zu umgehen:

if exists (select * from sys.procedures where name = 'sp_test')
    drop procedure sp_test
GO
create procedure sp_test as
begin
    select 1
end

Dies ist sehr praktisch, wenn Sie beispielsweise möchten, dass ein einzelnes SQL-Skript in der Quellcodeverwaltung eine gespeicherte Prozedur oder Funktion verwaltet. Wir verwenden dieses Muster häufig.

Eine weitere interessante Möglichkeit besteht darin, eine Abfrage mehrmals auszuführen:

INSERT INTO MyTable (...) ...
GO 10 -- run all the above 10 times!

Wie die Antworten auf diese SO-Frage zeigen, können Sie sie auch nach Ihren Wünschen konfigurieren. Wenn Sie sich mit Ihren Mitarbeitern anlegen möchten, setzen Sie das Batch-Trennzeichen auf "WHERE" anstelle von "GO". Spaß! :) :)

Paul Williams
quelle
13
Diese Art von Spaß kann gesundheitsschädlich sein.
Dylan Brams
2
Sie meinen, GO umgeht den Fehler, der nur existiert, weil GO existiert? Wenn Intellisense erkennen kann, dass GO erforderlich ist, kann dies sicherlich auch der Server tun und nur das Problem behandeln. Für mich ist logisch nichts falsch mit "Wenn vorhanden, löschen und danach erstellen". In den Dokumenten wird darauf hingewiesen, dass alle Anweisungen innerhalb eines GO-Stapels in einem einzigen Ausführungsplan zusammengefasst sind. Dies ist der Grund, warum diese Anweisungen fehlschlagen würden. Wenn Intellisense dies jedoch erkennen kann, kann dies sicherlich auch der Server und nur damit umgehen. go 10ist in der Tat nützlich - aber goin Ihrem früheren Beispiel nicht zwingend erforderlich.
youcantryreachingme
@youcantryreachingme Ich weiß nicht, warum SQL Server erfordert, dass diese Stapel getrennt sind. Wenn Ihnen das Verhalten nicht gefällt, können Sie wahrscheinlich ein Feedback für das SQL Server-Team eingeben. Es scheint mir eine seltsame Anforderung zu sein, aber ich bin sicher, dass es einen technischen Grund dafür gibt.
Paul Williams
@PaulWilliams - Nachdem ich alle meine Fragen in Kommentaren hier (und zum DBA-Stack-Austausch) beantwortet hatte, durchsuchte ich immer wieder Microsoft-Dokumente, um zu versuchen, sie zu verstehen. Ich denke, das "Warum" - die Gründe - für Stapel und gohängt vom Verständnis der Auswirkungen des Stapelns ab - in erster Linie, dass ein einzelner Ausführungsplan aus dem gesamten Stapel erstellt wird. Also entschied ich mich schließlich, meine eigene Antwort beizutragen, um zu versuchen, das "Warum" zu erklären - siehe unten, hier: stackoverflow.com/a/56370223/3714936
youcantryreachingme
14

Was bringt ein Batch-Separator?

Nachdem ich viele der Antworten gelesen und zu Kommentaren beigetragen habe, denke ich Folgendes.

Die eigentliche Frage lautet: "Was bringt es, eine Charge zu haben?"

Es gibt zwei Implikationen der Stapelverarbeitung, die eine gewisse Bedeutung haben, und es gibt eine zusätzliche Verwendung von go , die nützlich sein kann:

1. Alle Anweisungen in einem Stapel werden in einem einzigen Ausführungsplan zusammengefasst

Wie sich dies auf Sie als SQL-Entwickler auswirkt, weiß ich nicht. Aber da ist es. Dies hat zur Folge, dass Sie einige Anweisungen nicht im selben Stapel haben können . Beispielsweise können Sie in ALTEReiner Tabelle keine Spalte hinzufügen, dann selectdiese Spalte im selben Stapel - weil erstellen, da diese Spalte beim Kompilieren des Ausführungsplans nicht zur Auswahl vorhanden ist.

Ich denke, es gibt ein offenes Argument dafür, ob SQL Server dies selbst erkennen kann, ohne dass Entwickler goAnweisungen in ihre Skripte aufnehmen müssen. Darüber hinaus heißt es in den Dokumenten, dass ODBC-Verbindungen möglicherweise niemals einen goBefehl ausgeben . Mir ist nicht klar, wie sich ein über ODBC ausgeführtes Skript verhalten würde, wenn es das gerade angegebene ALTER/ -Beispiel enthalten würde SELECT.

2. Lokal deklarierte Variablen existieren nur im Rahmen des Stapels, in dem sie deklariert wurden

Diese beiden Punkte zusammen saugen irgendwie. Ich habe ein Skript, das DB-Strukturen (Tabellen, Prozeduren usw.) erstellt und ändert, und ich möchte zu Beginn des Skripts Variablen deklarieren, die zur Steuerung des Verhaltens des Skripts insgesamt verwendet werden. Sobald ich eine Charge einpacken muss (z. B. aufgrund einerALTER Anweisung - siehe Punkt 1 oben), fallen diese "Konfigurations" -Variablen aus dem Gültigkeitsbereich und können nicht weiter unten im Skript verwendet werden. Meine Problemumgehung besteht darin, eine Tabelle zu erstellen, die Konfigurationsvariablen in der Tabelle beizubehalten, sie dann vollständig durch mein Skript zu lesen und die Tabelle am Ende zu löschen (falls jemand anderes damit konfrontiert ist).

Diese zweite Implikation kann tatsächlich zum Vorteil genutzt werden. Wenn Ihr Skript viel Arbeit leistet und Sie einfach alle Ihre lokalen Variablen löschen möchten, können Sie einfach eine GOAnweisung einfügen und dann neue Variablen deklarieren (dh die wiederverwenden) gleiche Namen, wenn Sie das wollen).

3. GO verfügt über einen optionalen Parameter (mit dem Namen "count"), der den Server anweist, die Stapelaktionen mehrmals zu wiederholen

Diese Verwendung scheint eine nette zusätzliche Funktionalität zu sein, die der GOAnweisung hinzugefügt wurde . Ich glaube, die anfängliche oder primäre Funktion von GObezieht sich eher auf die Erstellung eines einzelnen Ausführungsplans, wie in Punkt 1 erwähnt - ansonsten könnte das Schlüsselwort auch so etwas wie sein REPEAT 10- aber was wiederholen? Der Stapel. Ohne GOdie Angabe eines Stapels konnte ein Wiederholungsbefehl immer nur die vorherige einzelne Anweisung wiederholen. Daher GOist eine gute Möglichkeit, Chargen zu wiederholen .

Referenz

All dies ergibt sich aus dem Versuch, die MS-Dokumentation zu GO zu verstehen . Viele der anderen Antworten - hier und bei anderen Fragen - greifen Teile der Dokumentation auf, aber ich denke, die Dokumentation selbst erklärt nicht wirklich, warum das Stapeln überhaupt einen Vorteil hat - daher mein Beitrag zu einem bereits gut kommentierten Frage.

Nachtrag

Nachdem ich das oben Gesagte geschrieben hatte , fand ich die von Microsoft in der GODokumentation erwähnten Regeln für die Verwendung von Stapeln . Auf der verlinkten Seite wird erläutert, dass ein Ausführungsplan aus mehreren Anweisungen besteht. Es heißt auch, dass einzelne Anweisungen in einen neuen Ausführungsplan neu kompiliert werden können (dh von SQL Server, während der Stapel automatisch verarbeitet wird). Wenn Sie beispielsweise einer Anweisung folgen, CREATE TABLEwird möglicherweise eine INSERTin diese Tabelle aufgenommen. Diese INSERTAnweisung wird neu kompiliert, nachdem die Tabelle in der vorherigen Anweisung erstellt wurde.

Dies verstärkt die Idee, dass SQL Server wahrscheinlich jene Szenarien erkennen könnte, in denen ALTERauf eine Tabelle eine folgt, SELECTund dass die Datei neu kompiliert werden muss SELECT(siehe Punkt 1 oben), und möglicherweise passiert genau dies, wenn ODBC verwendet wird (siehe Punkt 1 oben).

Keine dieser neuen Informationen ändert die 3 oben genannten Punkte. Der Link, den ich gerade gegeben habe, enthält zusätzliche Lektüre und endet mit "den Regeln", die diese sind:

  • Die Anweisungen CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER und CREATE VIEW können nicht mit anderen Anweisungen in einem Stapel kombiniert werden. Die Anweisung CREATE muss den Stapel starten. Alle anderen Anweisungen, die in diesem Stapel folgen, werden als Teil der Definition der ersten CREATE-Anweisung interpretiert.

  • Eine Tabelle kann nicht geändert werden, und dann werden die neuen Spalten im selben Stapel referenziert.

  • Wenn eine EXECUTE-Anweisung die erste Anweisung in einem Stapel ist, ist das Schlüsselwort EXECUTE nicht erforderlich. Das Schlüsselwort EXECUTE ist erforderlich, wenn die Anweisung EXECUTE nicht die erste Anweisung im Stapel ist.

youcantryreachingme
quelle
Das war unglaublich nützlich. Vielen Dank, dass Sie sich die Zeit genommen haben, das Gelernte gut organisiert zu teilen.
NetherGranite
5

Wie Martain sagte, müssen Anweisungen wie CREATE PROCEDURE die einzigen in einer Charge sein.

Beispielsweise verwende ich Stapeltrennzeichen, wenn ich gespeicherte Prozeduren erstelle und einem bestimmten Benutzer Berechtigungen hinzufüge. Wenn ich das "Los" weglassen würde, würde ich eine gespeicherte Prozedur erhalten, die bei jeder Ausführung Rechte gewährt. Auf diese Weise kann ich sie gleichzeitig schreiben und sicherstellen, dass ich keine gespeicherten Prozeduren schreibe, die beim Aufrufen brechen. Zum Beispiel

create procedure [procedurename]
(parameters)
as begin

select prefname, lastname from people

end

go

grant execute on [procedurename] to [username]
DaneEdw
quelle