Pourquoi ne puis-je pas utiliser des variables en T-SQL comme je l'imagine?

18

Pardonnez-moi, je suis un développeur qui est passé au monde de SQL. Je pensais que je pouvais améliorer certains SQL en ajoutant des variables mais cela ne fonctionnait pas comme je m'y attendais. Quelqu'un peut-il me dire pourquoi cela ne fonctionne pas? Je ne veux pas de travail, je veux connaître les raisons pour lesquelles cela ne fonctionne pas comme je l'imagine, car je suis sûr qu'il y a une bonne raison, mais actuellement, cela ne me saute pas aux yeux.

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

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
gareth
la source
Vous devez utiliser SQL dynamique pour cela. mssqltips.com/sqlservertip/1160/…
Stijn Wynants
3
Hé merci d'avoir commenté mais selon ma question, ce n'est pas la réponse que je cherche. Je veux savoir si quelqu'un sait pourquoi je ne peux pas le faire comme je l'ai montré.
gareth
2
Parce qu'il n'est pas possible d'utiliser la USEcommande avec un paramètre.
TT.
2
En plus des réponses déjà données, mais pas suffisantes pour une réponse qui lui est propre, les variables sont étendues au maximum du lot actuel. (Je ne sais pas s'il est possible d'étendre les variables de manière plus étroite que dans SQL Server.) Donc, au moment où vous avez le GO, la variable précédemment déclarée disparaît. Vous souhaiterez peut-être examiner les variables SQLCMD, qui peuvent ou non s'appliquer à votre scénario.
un CVn du
1
Nous avons tendance à considérer les noms de base de données et les noms de colonne comme des valeurs de chaîne, mais dans le contexte de SQL, ce sont des identificateurs. Ce que vous essayez serait la même chose que d’attendre x = 7; être identique à «x» = 7; dans une autre langue. Tout comme un langage informatique pourrait être créé qui traite avec «x» = 7 de la même manière que x = 7, un SGBDR pourrait être créé qui traite Créer une table X de la même manière que Créer une table «X». Mais ce ne serait pas SQL.
user1008646

Réponses:

20

Par la page en ligne de livres pour les variables

Les variables ne peuvent être utilisées que dans les expressions, pas à la place des noms d'objets ou des mots clés. Pour construire des instructions SQL dynamiques, utilisez EXECUTE.

Cela fonctionnerait comme vous l'attendiez si, par exemple, vous utilisiez votre variable dans une clause where. Quant à savoir pourquoi, je pense que cela a quelque chose à voir avec l'analyseur incapable d'évaluer la variable et donc de vérifier son existence. Lors de l'exécution, la requête est d'abord analysée pour la syntaxe et les objets, puis, si l'analyse réussit, la requête s'exécute à quel point la variable serait définie.

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
la source
3
Notez que le SQL dynamique doit être évité autant que possible. C'est l'analogue SQL de l'utilisation de evalfonctions dans des langages procéduraux comme JavaScript et Python. C'est un moyen rapide de créer des failles de sécurité.
jpmc26
1
@ jpmc26: Quelle est la façon la plus sûre de le faire qui n'implique pas le SQL dynamique?
Robert Harvey
1
@RobertHarvey Ce n'est pas parce qu'il vaut mieux l'éviter qu'il y a toujours une alternative avec exactement la même fonctionnalité. ;) Souvent, une partie de la réponse est: "Utilisez une solution complètement différente au problème." Parfois, c'est la meilleure chose à faire, mais non sans une bonne quantité de délibérations et de s'assurer que vous n'avez pas négligé les alternatives, et même alors, cela devrait venir avec une bonne dose de prudence.
jpmc26
2
@ jpmc26: L'exemple de l'OP ressemble à ce qu'un ORM "Code-First" pourrait faire pour configurer des tables dans une base de données. Bien que le SQL dynamique ne soit pas sûr en principe, l'utilisateur final ne toucherait jamais à ce code particulier.
Robert Harvey
@RobertHarvey Dépend de qui vous considérez comme "l'utilisateur final". Pour un script qui déploie une base de données, je considérerais le développeur et peut-être certains administrateurs système comme "l'utilisateur final". Je continuerais de concevoir un système pour rejeter les entrées non sécurisées dans ce cas, ne serait-ce que pour éviter les accidents. De plus, comme pour "ne jamais toucher", l'OP touche ce code, donc ...
jpmc26
17

Les limitations de l'utilisation des variables dans les instructions SQL proviennent de l'architecture de SQL.

Le traitement d'une instruction SQL se déroule en trois phases:

  1. Préparation - L'instruction est analysée et un plan d'exécution est compilé, spécifiant quels objets de base de données sont accédés, comment ils sont accédés et comment ils sont liés. Le plan d'exécution est enregistré dans le cache du plan .
  2. Liaison - toutes les variables de l'instruction sont remplacées par des valeurs réelles.
  3. Exécution - le plan mis en cache est exécuté avec les valeurs liées.

Le serveur SQL cache l'étape de préparation au programmeur et l'exécute beaucoup plus rapidement que les bases de données plus traditionnelles telles qu'Oracle et DB2. C'est pour des raisons de performances que SQL passe potentiellement beaucoup de temps à déterminer un plan d'exécution optimal, mais uniquement la première fois que l'instruction est rencontrée après un redémarrage.

Ainsi, en SQL statique , les variables ne peuvent être utilisées que dans des endroits où elles n'invalideront pas le plan d'exécution, donc pas pour les noms de table, les noms de colonne (y compris les noms de colonne dans les conditions WHERE), etc.

Le SQL dynamique existe pour les cas où l'on ne peut pas contourner les restrictions, et le programmeur sait que son exécution prendra un peu plus de temps. Le SQL dynamique peut être vulnérable à l'injection de code malveillant, alors faites attention!

grahamj42
la source
7

Comme vous pouvez le voir, la question du «pourquoi» nécessite un type de réponse différent, y compris une justification historique et des hypothèses sous-jacentes à la langue, je ne suis pas sûr de pouvoir vraiment rendre justice.

Cet article complet par SQL MVP Erland Sommarskog tente de fournir une justification, ainsi que la mécanique:

La malédiction et les bénédictions du SQL dynamique :

Plans de requête de mise en cache

Chaque requête que vous exécutez dans SQL Server nécessite un plan de requête. Lorsque vous exécutez une requête pour la première fois, SQL Server crée un plan de requête pour celle-ci - ou, selon la terminologie, il compile la requête. SQL Server enregistre le plan dans le cache et la prochaine fois que vous exécutez la requête, le plan est réutilisé.

Ceci (et la sécurité, voir ci-dessous) est probablement la principale raison.

SQL fonctionne en partant du principe que les requêtes ne sont pas des opérations ponctuelles, mais qu'elles seront utilisées à plusieurs reprises. Si la table (ou la base de données!) N'est pas réellement spécifiée dans la requête, elle n'a aucun moyen de générer et d'enregistrer un plan d'exécution pour une utilisation future.

Oui, toutes les requêtes que nous exécutons ne seront pas réutilisées, mais c'est la prémisse d'exploitation par défaut de SQL , donc les "exceptions" sont censées être exceptionnelles.

Erland énumère quelques autres raisons (notez qu'il énumère explicitement les avantages de l'utilisation des procédures stockées , mais beaucoup d'entre elles sont également des avantages des requêtes paramétrées (non dynamiques)):

  • Le système d'autorisation : le moteur SQL ne peut pas prédire si vous avez le droit d'exécuter une requête s'il ne connaît pas la table (ou la base de données) sur laquelle vous allez opérer. Les "chaînes d'autorisations" utilisant le SQL dynamique sont une douleur dans le cul.
  • Réduction du trafic réseau : la transmission du nom du proc stocké et de quelques valeurs de paramètres sur le réseau est plus courte qu'une longue requête.
  • Encapsulation de la logique : vous devez être familiarisé avec les avantages de l'encapsulation de la logique à partir d'autres environnements de programmation.
  • Garder une trace de ce qui est utilisé : Si je dois changer une définition de colonne, comment puis-je trouver tout le code qui l'appelle? Il existe des procédures système pour rechercher des dépendances dans une base de données SQL, mais uniquement si le code se trouve dans des procédures stockées.
  • Facilité d'écriture de code SQL : la vérification de la syntaxe se produit lorsque vous créez ou modifiez une procédure stockée, donc, espérons-le, moins d'erreurs en résultent.
  • Résolution des bogues et des problèmes : un administrateur de base de données peut tracer et mesurer les performances de chaque procédure stockée beaucoup plus facilement que le SQL dynamique en constante évolution.

Encore une fois, chacun d'eux a cent nuances que je n'entrerai pas ici.

BradC
la source
2

Vous devez utiliser SQL dynamique

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

ci-dessous est la sortie de l'impression .. une fois que vous ne commentez pas exec sp_executesql @sqltextles instructions seront effectivement exécutées ...

CREATE DATABASE [dbamaint];
use [dbamaint];
Kin Shah
la source
1
Oui merci, je le sais, mais je veux savoir si quelqu'un sait pourquoi vous ne pouvez pas simplement utiliser la variable?
gareth
L'analyseur T-SQL générera des erreurs de syntaxe. Ce n'est pas un T-SQL valide que l'analyseur reconnaît.
Kin Shah du
Merci Kin, je suis sûr qu'il doit y avoir de bonnes raisons. Peut-être parce que les noms de base de données peuvent contenir «@» et probablement d'autres raisons plus complexes.
gareth
1
Oui, ils peuvent contenir @ et je pense que c'est la raison principale. msdn.microsoft.com/en-us/library/ms175874.aspx
Paweł Tajs
1
@gazeranco Croyez-moi, quiconque travaille avec SQL Server souhaite sans aucun doute que plus de commandes acceptent des variables à la place d'identificateurs constants.
db2