Warum kann ich in T-SQL keine Variablen verwenden, wie ich es mir vorstelle?

18

Verzeih mir, ich bin ein Entwickler, der in die Welt von SQL gewechselt ist. Ich dachte, ich könnte etwas SQL verbessern, indem ich Variablen hinzufüge, aber es funktionierte nicht so, wie ich es erwartet hatte. Kann mir jemand sagen warum das nicht geht? Ich möchte keine Abhilfe schaffen, ich möchte die Gründe wissen, warum dies nicht so funktioniert, wie ich es mir vorstelle, da ich mir sicher bin, dass es einen guten Grund gibt, aber momentan springt es mir nicht auf die Nerven.

DECLARE @DatabaseName varchar(150)
SET @DatabaseName = 'MyAmazingDatabaseName'

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
gareth
quelle
Sie müssen dafür dynamisches SQL verwenden. mssqltips.com/sqlservertip/1160/…
Stijn Wynants
3
Hey, danke für den Kommentar, aber gemäß meiner Frage ist dies nicht die Antwort, nach der ich suche. Ich möchte wissen, ob jemand weiß, warum ich es nicht so machen kann, wie ich es gezeigt habe.
Gareth
2
Weil es nicht möglich ist, den USEBefehl mit einem Parameter zu verwenden.
TT.
2
Zusätzlich zu den bereits gegebenen Antworten, die für eine eigene Antwort jedoch nicht ausreichen, werden Variablen auf ein Maximum des aktuellen Stapels festgelegt. (Ich weiß nicht genau, ob es möglich ist, Variablen enger als in SQL Server einzuschränken.) In dem Moment, in dem Sie die haben GO, verschwindet die zuvor deklarierte Variable. Möglicherweise möchten Sie sich mit SQLCMD-Variablen befassen, die möglicherweise für Ihr Szenario gelten oder nicht.
ein Lebenslauf vom
1
Wir neigen dazu, Datenbanknamen und Spaltennamen als Zeichenfolgenwerte zu betrachten, aber im Kontext von SQL sind sie Bezeichner. Was Sie versuchen, entspricht der Erwartung von x = 7; dasselbe sein wie 'x' = 7; in einer anderen Sprache. So wie eine Computersprache erstellt werden kann, die mit 'x' = 7 dasselbe wie x = 7 umgeht, kann ein RDBMS erstellt werden, das Create Table X genauso behandelt wie Create Table 'X'. Aber das wäre nicht SQL.
user1008646

Antworten:

20

Per der Books Online-Seite für Variablen

Variablen können nur in Ausdrücken verwendet werden, nicht anstelle von Objektnamen oder Schlüsselwörtern. Verwenden Sie EXECUTE, um dynamische SQL-Anweisungen zu erstellen.

Es würde so funktionieren, wie Sie es erwartet hatten, wenn Sie beispielsweise Ihre Variable in einer where-Klausel verwenden würden. Was den Grund angeht, so hat dies vermutlich etwas mit dem Parser zu tun, der die Variable nicht auswerten und somit auf Existenz prüfen kann. Bei der Ausführung wird die Abfrage zuerst nach Syntax und Objekten analysiert. Wenn die Analyse erfolgreich ist, wird die Abfrage ausgeführt, an welcher Stelle die Variable gesetzt wird.

DECLARE @name varchar(20);
SET @name = 'test';

CREATE TABLE [#tmp]([val] varchar(10));

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;
Bob Klimes
quelle
3
Beachten Sie, dass dynamisches SQL nach Möglichkeit vermieden werden sollte. Es ist das SQL-Analogon zur Verwendung von evalFunktionen in prozeduralen Sprachen wie JavaScript und Python. Es ist eine schnelle Möglichkeit, Sicherheitslücken zu schaffen.
jpmc26
1
@ jpmc26: Was ist die sicherere Methode, die kein dynamisches SQL beinhaltet?
Robert Harvey
1
@RobertHarvey Nur weil es am besten vermieden wird, heißt das nicht, dass es immer eine Alternative mit genau der gleichen Funktionalität gibt. ;) Oft lautet ein Teil der Antwort: "Verwenden Sie eine völlig andere Lösung für das Problem." Manchmal ist es das Beste, aber nicht ohne gründliche Überlegungen und um sicherzustellen, dass Sie Alternativen nicht vernachlässigt haben, und selbst dann sollte es mit einer gesunden Dosis Vorsicht kommen.
jpmc26
2
@ jpmc26: Das Beispiel des OP sieht so aus, als ob ein "Code-First" -ORM zum Einrichten von Tabellen in einer Datenbank verwendet werden könnte. Während dynamisches SQL im Prinzip unsicher ist, würde der Endbenutzer diesen bestimmten Code niemals berühren.
Robert Harvey
@RobertHarvey Hängt davon ab, wen Sie als "Endbenutzer" betrachten. Für ein Skript, das eine Datenbank bereitstellt, würde ich den Entwickler und möglicherweise einige Systemadministratoren als "Endbenutzer" betrachten. Ich würde das System immer noch so auslegen, dass unsichere Eingaben in diesem Fall zurückgewiesen werden, und sei es nur, um Unfälle zu vermeiden. Auch was "nie berühren"
betrifft,
17

Die Einschränkungen bei der Verwendung von Variablen in SQL-Anweisungen ergeben sich aus der Architektur von SQL.

Die Verarbeitung einer SQL-Anweisung umfasst drei Phasen:

  1. Vorbereitung - Die Anweisung wird analysiert und ein Ausführungsplan erstellt, in dem angegeben wird, auf welche Datenbankobjekte zugegriffen wird, wie auf sie zugegriffen wird und in welcher Beziehung sie zueinander stehen. Der Ausführungsplan wird im Plan-Cache gespeichert .
  2. Bindung - Alle Variablen in der Anweisung werden durch tatsächliche Werte ersetzt.
  3. Ausführung - Der zwischengespeicherte Plan wird mit den gebundenen Werten ausgeführt.

SQL Server verbirgt den Vorbereitungsschritt vor dem Programmierer und führt ihn viel schneller aus als herkömmliche Datenbanken wie Oracle und DB2. Aus Performancegründen verbringt SQL möglicherweise viel Zeit damit, einen optimalen Ausführungsplan zu bestimmen, dies jedoch nur, wenn die Anweisung zum ersten Mal nach einem Neustart auftritt.

In statischem SQL dürfen Variablen daher nur an Stellen verwendet werden, an denen sie den Ausführungsplan nicht ungültig machen, also nicht für Tabellennamen, Spaltennamen (einschließlich Spaltennamen in WHERE-Bedingungen) usw.

Dynamisches SQL gibt es für Fälle, in denen man die Einschränkungen nicht umgehen kann und der Programmierer weiß, dass die Ausführung etwas länger dauert. Dynamic SQL kann anfällig für bösartigen Code sein. Seien Sie also vorsichtig!

grahamj42
quelle
7

Wie Sie sehen, erfordert die "Warum" -Frage eine andere Art von Antwort, einschließlich historischer Gründe und zugrunde liegender Annahmen für die Sprache. Ich bin mir nicht sicher, ob ich dieser Frage wirklich gerecht werden kann.

In diesem umfassenden Artikel von SQL MVP Erland Sommarskog wird versucht, einige Gründe zusammen mit den Mechanismen anzugeben:

Der Fluch und der Segen von Dynamic SQL :

Abfragepläne zwischenspeichern

Für jede Abfrage, die Sie in SQL Server ausführen, ist ein Abfrageplan erforderlich. Wenn Sie eine Abfrage zum ersten Mal ausführen, erstellt SQL Server einen Abfrageplan für diese Abfrage. Je nach Terminologie wird die Abfrage kompiliert. SQL Server speichert den Plan im Cache. Wenn Sie die Abfrage das nächste Mal ausführen, wird der Plan wiederverwendet.

Dies (und die Sicherheit, siehe unten) ist wahrscheinlich der Hauptgrund.

SQL geht davon aus, dass Abfragen keine einmaligen Vorgänge sind, sondern immer wieder verwendet werden. Wenn die Tabelle (oder die Datenbank!) Nicht tatsächlich in der Abfrage angegeben ist, kann kein Ausführungsplan für die zukünftige Verwendung generiert und gespeichert werden.

Ja, nicht jede Abfrage, die wir ausführen, wird wiederverwendet. Dies ist jedoch die Standardvoraussetzung für SQL , sodass "Ausnahmen" als außergewöhnlich angesehen werden.

Einige andere Gründe, die Erland aufführt (beachten Sie, dass er die Vorteile der Verwendung gespeicherter Prozeduren explizit auflistet , aber viele davon sind auch Vorteile parametrisierter (nicht dynamischer) Abfragen):

  • Das Berechtigungssystem : Die SQL-Steuerkomponente kann nicht vorhersagen, ob Sie zum Ausführen einer Abfrage berechtigt sind, wenn sie die Tabelle (oder Datenbank) nicht kennt, mit der Sie arbeiten werden. "Berechtigungsketten" mit dynamischem SQL sind ein Problem.
  • Reduzieren des Netzwerkverkehrs : Das Übergeben des Namens des gespeicherten Prozesses und einiger Parameterwerte über das Netzwerk ist kürzer als eine lange Abfrageanweisung.
  • Kapselungslogik : Sie sollten mit den Vorteilen der Kapselung von Logik aus anderen Programmierumgebungen vertraut sein.
  • Verfolgen, was verwendet wird : Wenn ich eine Spaltendefinition ändern muss, wie kann ich den gesamten Code finden, der sie aufruft? Systemprozeduren sind vorhanden, um Abhängigkeiten in einer SQL-Datenbank zu finden, jedoch nur, wenn sich der Code in gespeicherten Prozeduren befindet.
  • Einfache Erstellung von SQL-Code : Die Syntaxprüfung erfolgt beim Erstellen oder Ändern einer gespeicherten Prozedur, sodass hoffentlich weniger Fehler auftreten.
  • Beheben von Fehlern und Problemen : Ein DBA kann die Leistung einzelner gespeicherter Prozeduren viel einfacher verfolgen und messen als sich ständig änderndes dynamisches SQL.

Auch hier hat jede hundert Nuancen, auf die ich hier nicht eingehen werde.

BradC
quelle
2

Sie müssen dynamisches SQL verwenden

DECLARE @DatabaseName varchar(150) = 'dbamaint'
declare @sqltext nvarchar(max) = N''

set @sqltext = N'CREATE DATABASE '+quotename(@DatabaseName)+ ';'

print @sqltext 

-- once you are happy .. uncomment below
--exec sp_executesql @sqltext
set @sqltext = ''
set @sqltext = N'use '+quotename(@DatabaseName)+ ';'
print @sqltext 
-- once you are happy .. uncomment below
--exec sp_executesql @sqltext

Unten ist die Ausgabe von print. Sobald Sie das Kommentarzeichen entfernen, werden exec sp_executesql @sqltextdie Anweisungen tatsächlich ausgeführt.

CREATE DATABASE [dbamaint];
use [dbamaint];
Kin Shah
quelle
1
Ja danke, das weiß ich, aber ich möchte wissen, ob jemand weiß, warum Sie die Variable nicht einfach verwenden können?
Gareth
Der T-SQL-Parser löst Syntaxfehler aus. Es ist keine gültige T-SQL, die der Parser erkennt.
Kin Shah
Danke Kin, ich bin mir sicher, dass es gute Gründe dafür geben muss. Möglicherweise, weil Datenbanknamen '@' und möglicherweise einige komplexere Gründe enthalten können.
Gareth
1
Ja, sie können @ enthalten und ich denke, das ist der Hauptgrund. msdn.microsoft.com/en-us/library/ms175874.aspx
Paweł Tajs
1
@gazeranco Glauben Sie mir, jeder, der mit SQL Server arbeitet, wünscht sich zweifellos, dass mehr Befehle Variablen anstelle von konstanten Bezeichnern akzeptieren.
db2