Por que não consigo usar variáveis ​​no T-SQL como imagino que posso?

18

Perdoe-me, sou um desenvolvedor que se mudou para o mundo do SQL. Eu pensei que poderia melhorar um pouco de SQL adicionando variáveis, mas não funcionou como eu esperava. Alguém pode me dizer por que isso não funciona? Não quero uma solução alternativa, quero saber as razões pelas quais isso não funciona como imagino que deva, pois tenho certeza de que há uma boa razão, mas, atualmente, não me ocorre.

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

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
gareth
fonte
Você precisa usar sql dinâmico para isso. mssqltips.com/sqlservertip/1160/...
Stijn Wynants
3
Ei, obrigado por comentar, mas de acordo com a minha pergunta, esta não é a resposta que estou procurando. Quero saber se alguém sabe por que não posso fazer isso, como mostrei.
Gareth
2
Porque não é possível usar o USEcomando com um parâmetro
TT.
2
Além das respostas já fornecidas, mas não suficientes para uma resposta própria, as variáveis ​​têm um escopo definido no máximo para o lote atual. (Não sei de antemão se é possível definir variáveis ​​de forma mais restrita que no SQL Server.) Portanto, no momento em que você possui GO, a variável declarada anteriormente desaparece. Você pode querer analisar as variáveis ​​SQLCMD, que podem ou não ser aplicáveis ​​ao seu cenário.
um CVn
11
Tendemos a pensar nos nomes dos bancos de dados e nos nomes das colunas como valores de string, mas no contexto do SQL eles são Identificadores. O que você está tentando seria o mesmo que esperar x = 7; ser o mesmo que 'x' = 7; em algum outro idioma. Assim como pode ser criada uma linguagem de computador que lide com 'x' = 7 da mesma forma que x = 7, um RDBMS pode ser criado para tratar Criar Tabela X da mesma forma que Criar Tabela 'X'. Mas isso não seria SQL.
User1008646

Respostas:

20

De acordo com a página online do Books para variáveis

As variáveis ​​podem ser usadas apenas em expressões, não no lugar de nomes de objetos ou palavras-chave. Para construir instruções SQL dinâmicas, use EXECUTE.

Funcionaria da maneira que você esperava se, por exemplo, você usasse sua variável em uma cláusula where. Quanto ao porquê, eu acho que tem algo a ver com o analisador que não é capaz de avaliar a variável e, assim, verificar a existência. Ao executar, a consulta é analisada primeiro para sintaxe e objetos e, em seguida, se a análise for bem-sucedida, a consulta é executada no ponto em que a variável seria definida.

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
fonte
3
Observe que o SQL dinâmico deve ser evitado sempre que possível. É o analógico SQL do uso de evalfunções em linguagens procedurais como JavaScript e Python. É uma maneira rápida de criar falhas de segurança.
jpmc26
11
@ jpmc26: Qual é a maneira mais segura de fazer isso que não envolve SQL dinâmico?
Robert Harvey
11
@RobertHarvey Só porque é melhor evitar, não significa que sempre há uma alternativa com exatamente a mesma funcionalidade. ;) Muitas vezes, parte da resposta é: "Use uma solução completamente diferente para o problema". Às vezes, é a melhor coisa a se fazer, mas não sem uma boa quantidade de deliberação e garantia de que você não negligenciou alternativas e, mesmo assim, deve vir com uma dose saudável de cautela.
jpmc26
2
@ jpmc26: O exemplo do OP parece com o que um ORM "Code-First" pode fazer para configurar tabelas em um banco de dados. Embora o SQL dinâmico seja inseguro em princípio, o usuário final nunca tocaria nesse código específico.
Robert Harvey
@RobertHarvey Depende de quem você considera o "usuário final". Para um script que implanta um banco de dados, considero o desenvolvedor e possivelmente alguns administradores de sistema o "usuário final". Eu ainda projetaria um sistema para rejeitar informações inseguras nesse caso, se não por outro motivo a não ser evitar acidentes. Além disso, como para "não toque", o OP está tocando este código, então ...
jpmc26
17

As limitações no uso de variáveis ​​nas instruções SQL surgem da arquitetura do SQL.

Existem três fases no processamento de uma instrução SQL:

  1. Preparação - A instrução é analisada e um plano de execução é compilado, especificando quais objetos de banco de dados são acessados, como são acessados ​​e como estão relacionados. O plano de execução é salvo no cache do plano .
  2. Ligação - todas as variáveis ​​na instrução são substituídas pelos valores reais.
  3. Execução - o plano em cache é executado com os valores associados.

O servidor SQL oculta a etapa de preparação do programador e a executa muito mais rapidamente do que os bancos de dados mais tradicionais, como Oracle e DB2. É por razões de desempenho que o SQL gasta potencialmente muito tempo determinando um plano de execução ideal, mas somente na primeira vez que a instrução é encontrada após uma reinicialização.

Portanto, no SQL estático , as variáveis ​​podem ser usadas apenas em locais onde não invalidarão o plano de execução, portanto, não para nomes de tabelas, nomes de colunas (incluindo nomes de colunas nas condições WHERE), etc.

O SQL dinâmico existe para os casos em que não é possível contornar as restrições, e o programador sabe que levará um pouco mais de tempo para executar. O SQL dinâmico pode ser vulnerável à injeção de código malicioso, portanto, tome cuidado!

grahamj42
fonte
7

Como você pode ver, a pergunta "por que" requer um tipo diferente de resposta, incluindo a justificativa histórica e as suposições subjacentes ao idioma, não tenho certeza de que possa realmente fazer essa justiça.

Este artigo abrangente do MVP do SQL Erland Sommarskog tenta fornecer algumas justificativas, junto com a mecânica:

A maldição e as bênçãos do SQL dinâmico :

Cache de planos de consulta

Toda consulta que você executa no SQL Server requer um plano de consulta. Quando você executa uma consulta pela primeira vez, o SQL Server cria um plano de consulta para ela - ou conforme a terminologia - compila a consulta. O SQL Server salva o plano no cache e, da próxima vez que você executar a consulta, o plano será reutilizado.

Este (e segurança, veja abaixo) é provavelmente o maior motivo.

O SQL opera sob a premissa de que as consultas não são operações únicas, mas que serão usadas repetidamente. Se a tabela (ou o banco de dados!) Não estiver realmente especificada na consulta, ela não poderá gerar e salvar um plano de execução para uso futuro.

Sim, nem todas as consultas que executamos serão reutilizadas, mas essa é a premissa operacional padrão do SQL , portanto, as "exceções" devem ser excepcionais.

Algumas outras razões pelas quais Erland lista (observe que ele lista explicitamente as vantagens de usar procedimentos armazenados , mas muitos deles também são vantagens de consultas parametrizadas (não dinâmicas)):

  • O sistema de permissão : o mecanismo SQL não pode prever se você tem o direito de executar uma consulta se não souber a tabela (ou banco de dados) em que estará operando. "Cadeias de permissão" usando SQL dinâmico é um problema.
  • Reduzindo o tráfego de rede : passar o nome do processo armazenado e alguns valores de parâmetro na rede é mais curto que uma declaração de consulta longa.
  • Lógica de encapsulamento : você deve estar familiarizado com as vantagens de encapsular a lógica de outros ambientes de programação.
  • Acompanhar o que é usado : Se eu precisar alterar uma definição de coluna, como posso encontrar todo o código que a chama? Os procedimentos do sistema existem para localizar dependências em um banco de dados SQL, mas apenas se o código estiver em procedimentos armazenados.
  • Facilidade de escrever código SQL : a verificação de sintaxe ocorre quando você cria ou modifica um procedimento armazenado, resultando em menos erros.
  • Resolvendo Bugs e Problemas : Um DBA pode rastrear e medir o desempenho de procedimentos armazenados individuais com muito mais facilidade do que o SQL dinâmico em constante mudança.

Novamente, cada uma delas tem uma centena de nuances nas quais não vou entrar aqui.

BradC
fonte
2

Você precisa 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

abaixo está o resultado da impressão .. depois de descomentar, exec sp_executesql @sqltextas instruções serão realmente executadas ...

CREATE DATABASE [dbamaint];
use [dbamaint];
Kin Shah
fonte
11
Sim, obrigado, eu sei disso, mas quero saber se alguém sabe por que você não pode simplesmente usar a variável?
Gareth
O analisador T-SQL lançará erros de sintaxe. Não é um T-SQL válido que o analisador reconheça.
Kin Shah
Obrigado Kin, tenho certeza de que deve haver boas razões para isso. Talvez porque os nomes dos bancos de dados possam conter '@' e provavelmente outros motivos mais complexos.
Gareth
11
Sim, eles podem conter @ e acho que é o principal motivo. msdn.microsoft.com/pt-BR/library/ms175874.aspx
Paweł Tajs 12/12/16
11
@gazeranco Acredite, qualquer um que trabalhe com o SQL Server, sem dúvida, deseja que mais comandos aceitem variáveis ​​no lugar de identificadores constantes.
db2