Perché non posso usare le variabili in T-SQL come immagino di poter?


18

Perdonami, sono uno sviluppatore che si è trasferito nel mondo di SQL. Pensavo di poter migliorare alcuni SQL aggiungendo variabili ma non funzionava come mi aspettavo. Qualcuno può dirmi perché questo non funziona? Non voglio lavorare in giro, voglio sapere i motivi per cui questo non funziona come immagino che dovrebbe, dato che sono sicuro che ci sia una buona ragione, ma attualmente non mi salta addosso.

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

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO

È necessario utilizzare SQL dinamico per questo. mssqltips.com/sqlservertip/1160/…
Stijn Wynants

3
Ehi, grazie per aver commentato, ma secondo la mia domanda, questa non è la risposta che sto cercando. Voglio sapere se qualcuno sa perché non posso farlo come ho mostrato.
Gareth,

2
Perché non è possibile utilizzare il USEcomando con un parametro.
TT.

2
Oltre alle risposte già fornite, ma non sufficienti per una risposta a sé stante, le variabili sono impostate su un massimo del batch corrente. (Non so con la mano se è possibile eseguire l'ambito delle variabili in modo più stretto rispetto a quello in SQL Server.) Quindi nel momento in cui si ha la GO, la variabile precedentemente dichiarata scompare. Potresti voler esaminare le variabili SQLCMD, che possono o meno essere applicabili al tuo scenario.
un CVn del

1
Tendiamo a pensare ai nomi di database e ai nomi di colonna come valori di stringa, ma nel contesto di SQL sono identificatori. Quello che stai tentando sarebbe lo stesso di aspettarti x = 7; essere uguale a 'x' = 7; in qualche altra lingua. Proprio come potrebbe essere creato un linguaggio informatico che si occupa di 'x' = 7 come x = 7, è possibile creare un RDBMS che tratta Crea tabella X come Crea tabella 'X'. Ma quello non sarebbe SQL.
user1008646

Risposte:


20

Per la pagina online di Libri per le variabili

Le variabili possono essere utilizzate solo nelle espressioni, non al posto di nomi di oggetti o parole chiave. Per costruire istruzioni SQL dinamiche, utilizzare EXECUTE.

Funzionerebbe come ti aspettavi se, ad esempio, avessi usato la tua variabile in una clausola where. Per quanto riguarda il motivo, penso che abbia qualcosa a che fare con il parser non in grado di valutare la variabile e quindi verificare l'esistenza. Durante l'esecuzione, la query viene prima analizzata per la sintassi e gli oggetti e quindi, se l'analisi ha esito positivo, la query viene eseguita a quel punto in cui verrebbe impostata la variabile.

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

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

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;

3
Si noti che SQL dinamico dovrebbe essere evitato quando possibile. È l'analogo SQL all'utilizzo di evalfunzioni in linguaggi procedurali come JavaScript e Python. È un modo rapido per creare falle di sicurezza.
jpmc26,

1
@ jpmc26: qual è il modo più sicuro per farlo che non coinvolge SQL dinamico?
Robert Harvey,

1
@RobertHarvey Solo perché è meglio evitarlo non significa che c'è sempre un'alternativa con esattamente la stessa funzionalità. ;) Spesso, parte della risposta è "Usa una soluzione completamente diversa al problema". A volte è la cosa migliore da fare, ma non senza una buona dose di deliberazione e assicurandosi di non aver trascurato le alternative, e anche allora, dovrebbe venire con una buona dose di cautela.
jpmc26,

2
@ jpmc26: L'esempio dell'OP sembra quello che un ORM "Code-First" potrebbe fare per impostare le tabelle in un database. Mentre l'SQL dinamico non è sicuro in linea di principio, l'utente finale non toccherebbe mai quel particolare codice.
Robert Harvey,

@RobertHarvey Dipende da chi consideri "l'utente finale". Per uno script che distribuisce un DB, considererei lo sviluppatore e forse alcuni amministratori di sistema come "l'utente finale". Continuerei a progettare un sistema per rifiutare input non sicuri in quel caso, se non altro per evitare incidenti. Inoltre, per quanto riguarda "non toccare mai", l'OP sta toccando questo codice, quindi ...
jpmc26,

17

Le limitazioni all'uso delle variabili nelle istruzioni SQL derivano dall'architettura di SQL.

Esistono tre fasi nell'elaborazione di un'istruzione SQL:

  1. Preparazione: l'istruzione viene analizzata e viene compilato un piano di esecuzione , che specifica a quali oggetti del database si accede, come sono accessibili e come sono correlati. Il piano di esecuzione viene salvato nella cache del piano .
  2. Binding: qualsiasi variabile nell'istruzione viene sostituita con valori effettivi.
  3. Esecuzione: il piano memorizzato nella cache viene eseguito con i valori associati.

Il server SQL nasconde la fase di preparazione dal programmatore e lo esegue molto più velocemente rispetto ai database più tradizionali come Oracle e DB2. È per motivi di prestazioni che SQL impiega potenzialmente molto tempo a determinare un piano di esecuzione ottimale, ma lo fa solo la prima volta che si incontra l'istruzione dopo un riavvio.

Pertanto , nell'SQL statico , le variabili possono essere utilizzate solo in luoghi in cui non invalideranno il piano di esecuzione, quindi non per i nomi delle tabelle, i nomi delle colonne (inclusi i nomi delle colonne nelle condizioni WHERE), ecc.

SQL dinamico esiste per i casi in cui non è possibile aggirare le restrizioni e il programmatore sa che l'esecuzione richiederà leggermente più tempo. SQL dinamico può essere vulnerabile all'iniezione di codice dannoso, quindi fai attenzione!


7

Come puoi vedere, la domanda "perché" richiede un diverso tipo di risposta, tra cui la logica storica e le ipotesi alla base della lingua, non sono sicuro di poter davvero rendere giustizia.

Questo articolo completo di SQL MVP Erland Sommarskog tenta di fornire alcune motivazioni, insieme alla meccanica:

The Curse and Blessings of Dynamic SQL :

Piani di query nella cache

Ogni query eseguita in SQL Server richiede un piano di query. Quando si esegue una query per la prima volta, SQL Server crea un piano di query per esso - o secondo la terminologia - compila la query. SQL Server salva il piano nella cache e alla successiva esecuzione della query, il piano viene riutilizzato.

Questo (e la sicurezza, vedi sotto) è probabilmente il motivo principale.

SQL funziona con la premessa che le query non sono operazioni singole, ma che verranno utilizzate più e più volte. Se la tabella (o il database!) Non è effettivamente specificata nella query, non ha modo di generare e salvare un piano di esecuzione per uso futuro.

Sì, non tutte le query che eseguiremo verranno riutilizzate, ma questa è la premessa operativa predefinita di SQL , quindi "eccezioni" sono considerate eccezionali.

Alcuni altri motivi elencati da Erland (si noti che elenca esplicitamente i vantaggi dell'utilizzo di stored procedure , ma molti di questi sono anche vantaggi delle query con parametri (non dinamici):

  • Il sistema di autorizzazione : il motore SQL non è in grado di prevedere se si dispone dei diritti per eseguire una query se non conosce la tabella (o il database) su cui si opererà. Le "catene di autorizzazioni" che usano l'SQL dinamico sono una seccatura.
  • Riduzione del traffico di rete : il passaggio del nome del proc memorizzato e alcuni valori di parametro sulla rete sono più brevi di un'istruzione di query lunga.
  • Logica incapsulante : dovresti conoscere i vantaggi dell'incapsulamento della logica da altri ambienti di programmazione.
  • Tenere traccia di ciò che viene utilizzato : se devo modificare una definizione di colonna, come posso trovare tutto il codice che lo chiama? Esistono procedure di sistema per trovare dipendenze all'interno di un database SQL, ma solo se il codice è nelle stored procedure.
  • Facilità di scrittura del codice SQL : il controllo della sintassi si verifica quando si crea o si modifica una procedura memorizzata, quindi si spera che si verifichino meno errori.
  • Risoluzione di bug e problemi : un DBA può tracciare e misurare le prestazioni delle singole procedure memorizzate molto più facilmente rispetto all'SQL dinamico in continua evoluzione.

Ancora una volta, ognuna di queste ha cento sfumature che non entrerò qui.


2

Devi usare sql dinamico

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

di seguito è riportato l'output di stampa .. una volta decommentata, exec sp_executesql @sqltextle istruzioni verranno effettivamente eseguite ...

CREATE DATABASE [dbamaint];
use [dbamaint];

1
Sì grazie, lo so, ma voglio sapere se qualcuno sa perché non puoi semplicemente usare la variabile?
Gareth,

Il parser T-SQL genererà errori di sintassi. Non è un T-SQL valido riconosciuto dal parser.
Kin Shah,

Grazie Kin, sono sicuro che ci devono essere buoni motivi per questo. Forse perché i nomi dei database possono contenere "@" e probabilmente altri motivi più complessi.
Gareth,

1
Sì, possono contenere @ e penso che sia la ragione principale. msdn.microsoft.com/en-us/library/ms175874.aspx
Paweł Tajs

1
@gazeranco Credimi, chiunque lavori con SQL Server senza dubbio vorrebbe che più comandi accettassero le variabili al posto degli identificatori costanti.
db2,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.