¿Por qué no puedo usar variables en T-SQL como imagino que puedo?

18

Perdóname, soy un desarrollador que se ha mudado al mundo de SQL. Pensé que podría mejorar algunos SQL agregando variables, pero no funcionó como esperaba. ¿Alguien puede decirme por qué esto no funciona? No quiero una solución, quiero saber las razones por las que esto no funciona como imagino que debería, ya que estoy seguro de que hay una buena razón, pero actualmente no me sorprende.

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

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
gareth
fuente
Necesitas usar sql dinámico para eso. mssqltips.com/sqlservertip/1160/…
Stijn Wynants
3
Hola, gracias por comentar, pero según mi pregunta, esta no es la respuesta que estoy buscando. Quiero saber si alguien sabe por qué no puedo hacerlo como lo he demostrado.
Gareth
2
Porque no es posible usar el USEcomando con un parámetro.
TT.
2
Además de las respuestas ya dadas, pero no suficientes para una respuesta propia, las variables tienen un alcance máximo del lote actual. (No sé de antemano si es posible abarcar variables más estrechamente que eso en SQL Server). Entonces, en el momento en que tiene la GO, la variable declarada previamente desaparece. Es posible que desee analizar las variables SQLCMD, que pueden o no ser aplicables a su escenario.
un CVn
1
Tendemos a pensar en los nombres de bases de datos y columnas como valores de cadena, pero en el contexto de SQL son identificadores. Lo que está intentando sería lo mismo que esperar x = 7; ser lo mismo que 'x' = 7; en otro idioma Así como se podría crear un lenguaje de computadora que trate con 'x' = 7 lo mismo que x = 7, se podría crear un RDBMS que trate Crear tabla X igual que Crear tabla 'X'. Pero eso no sería SQL.
user1008646

Respuestas:

20

Por la página de libros en línea para variables

Las variables se pueden usar solo en expresiones, no en lugar de nombres de objetos o palabras clave. Para construir sentencias SQL dinámicas, use EXECUTE.

Funcionaría de la manera que esperaba si, por ejemplo, usara su variable en una cláusula where. En cuanto a por qué, creo que tiene algo que ver con el analizador no capaz de evaluar la variable y, por lo tanto, verificar su existencia. Al ejecutar, la consulta se analiza primero para la sintaxis y los objetos y luego, si el análisis es exitoso, la consulta se ejecuta en ese punto, la variable se establecería.

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
fuente
3
Tenga en cuenta que se debe evitar el SQL dinámico siempre que sea posible. Es el análogo de SQL para usar evalfunciones en lenguajes de procedimiento como JavaScript y Python. Es una forma rápida de crear agujeros de seguridad.
jpmc26
1
@ jpmc26: ¿Cuál es la forma más segura de hacerlo que no implique SQL dinámico?
Robert Harvey
1
@RobertHarvey El hecho de que sea mejor evitarlo no significa que siempre haya una alternativa con exactamente la misma funcionalidad. ;) Muchas veces, parte de la respuesta es: "Use una solución completamente diferente al problema". A veces es lo mejor que se puede hacer, pero no sin una buena cantidad de deliberaciones y de asegurarse de que no haya descuidado las alternativas, e incluso entonces, debe venir con una buena dosis de precaución.
jpmc26
2
@ jpmc26: El ejemplo del OP se parece a lo que un ORM "Code-First" podría hacer para configurar tablas en una base de datos. Si bien el SQL dinámico no es seguro en principio, el usuario final nunca tocaría ese código en particular.
Robert Harvey
@RobertHarvey Depende de a quién consideres el "usuario final". Para un script que implementa una base de datos, consideraría que el desarrollador y posiblemente algunos administradores del sistema son el "usuario final". Todavía diseñaría un sistema para rechazar la entrada insegura en ese caso, si no fuera por otra razón que para evitar accidentes. Además, como para "no tocar", el PO está en contacto con este código, así que ...
jpmc26
17

Las limitaciones en el uso de variables en las declaraciones SQL surgen de la arquitectura de SQL.

Hay tres fases en el procesamiento de una declaración SQL:

  1. Preparación: la instrucción se analiza y se compila un plan de ejecución , que especifica a qué objetos de la base de datos se accede, cómo se accede y cómo se relacionan. El plan de ejecución se guarda en la caché del plan .
  2. Enlace: todas las variables en la declaración se reemplazan con valores reales.
  3. Ejecución: el plan en caché se ejecuta con los valores enlazados.

El servidor SQL oculta el paso de preparación del programador y lo ejecuta mucho más rápido que las bases de datos más tradicionales como Oracle y DB2. Es por razones de rendimiento que SQL pasa potencialmente mucho tiempo determinando un plan de ejecución óptimo, pero solo lo hace la primera vez que se encuentra la declaración después de un reinicio.

Por lo tanto, en SQL estático , las variables solo se pueden usar en lugares donde no invaliden el plan de ejecución, por lo que no para nombres de tablas, nombres de columnas (incluidos los nombres de columnas en condiciones WHERE), etc.

El SQL dinámico existe para los casos en los que no se pueden evitar las restricciones, y el programador sabe que la ejecución tardará un poco más. El SQL dinámico puede ser vulnerable a la inyección de código malicioso, ¡así que tenga cuidado!

grahamj42
fuente
7

Como puede ver, la pregunta "por qué" requiere un tipo diferente de respuesta, que incluye la justificación histórica y los supuestos subyacentes para el lenguaje, no estoy seguro de que realmente pueda hacer esa justicia.

Este completo artículo del MVP de SQL Erland Sommarskog intenta proporcionar algunos fundamentos, junto con la mecánica:

La maldición y las bendiciones del SQL dinámico :

Planes de consulta de almacenamiento en caché

Cada consulta que ejecuta en SQL Server requiere un plan de consulta. Cuando ejecuta una consulta por primera vez, SQL Server crea un plan de consulta para ella, o según la terminología, compila la consulta. SQL Server guarda el plan en caché y la próxima vez que ejecute la consulta, el plan se reutiliza.

Esto (y la seguridad, ver más abajo) es probablemente la razón más importante.

SQL opera bajo la premisa de que las consultas no son operaciones únicas, sino que se utilizarán una y otra vez. Si la tabla (¡o la base de datos!) No se especifica realmente en la consulta, no tiene forma de generar y guardar un plan de ejecución para uso futuro.

Sí, no todas las consultas que ejecutamos se reutilizarán, pero esta es la premisa operativa predeterminada de SQL , por lo que las "excepciones" deben ser excepcionales.

Algunas otras razones que Erland enumera (tenga en cuenta que enumera explícitamente las ventajas de usar procedimientos almacenados , pero muchas de estas también son ventajas de consultas parametrizadas (no dinámicas)):

  • El sistema de permisos : el motor SQL no puede predecir si tiene los derechos para ejecutar una consulta si no conoce la tabla (o base de datos) con la que va a operar. Las "cadenas de permisos" que usan SQL dinámico son un fastidio.
  • Reducción del tráfico de red : pasar el nombre del proceso almacenado y algunos valores de parámetros a través de la red es más corto que una declaración de consulta larga.
  • Lógica de encapsulación : debe estar familiarizado con las ventajas de encapsular la lógica de otros entornos de programación.
  • Seguimiento de lo que se usa : si necesito cambiar la definición de una columna, ¿cómo puedo encontrar todo el código que la llama? Los procedimientos del sistema existen para encontrar dependencias dentro de una base de datos SQL, pero solo si el código está en procedimientos almacenados.
  • Facilidad de escritura del código SQL : la verificación de sintaxis se produce al crear o modificar un procedimiento almacenado, por lo que es de esperar que se produzcan menos errores.
  • Abordar errores y problemas : un DBA puede rastrear y medir el rendimiento de los procedimientos almacenados individuales mucho más fácilmente que el SQL dinámico que cambia constantemente.

Una vez más, cada uno de estos tiene un centenar de matices en los que no entraré aquí.

BradC
fuente
2

Necesitas usar sql dinámico

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

a continuación se muestra la salida de impresión ... una vez que elimine el comentario, exec sp_executesql @sqltextlas declaraciones se ejecutarán realmente ...

CREATE DATABASE [dbamaint];
use [dbamaint];
Kin Shah
fuente
1
Sí, gracias, lo sé, pero quiero saber si alguien sabe por qué no puedes usar la variable.
Gareth
El analizador T-SQL arrojará errores de sintaxis. No es un T-SQL válido que el analizador reconoce.
Kin Shah
Gracias Kin, estoy seguro de que debe haber buenas razones para ello. Tal vez porque los nombres de las bases de datos pueden contener '@' y probablemente otras razones más complejas.
gareth
1
Sí, pueden contener @ y creo que es la razón principal. msdn.microsoft.com/en-us/library/ms175874.aspx
Paweł Tajs
1
@gazeranco Créeme, cualquiera que trabaje con SQL Server sin duda desea que más comandos acepten variables en lugar de identificadores constantes.
db2