Declarar variável para uma string de consulta

92

Eu queria saber se havia uma maneira de fazer isso no MS SQL Server 2005:

  DECLARE @theDate varchar(60)
  SET @theDate = '''2010-01-01'' AND ''2010-08-31 23:59:59'''

  SELECT    AdministratorCode, 
            SUM(Total) as theTotal, 
            SUM(WOD.Quantity) as theQty, 
            AVG(Total) as avgTotal, 
            (SELECT SUM(tblWOD.Amount)
                FROM tblWOD
                JOIN tblWO on tblWOD.OrderID = tblWO.ID
                WHERE tblWO.Approved = '1' 
                AND tblWO.AdministratorCode = tblWO.AdministratorCode
                AND tblWO.OrderDate BETWEEN @theDate
            )
 ... etc

Isso é possível fazer?

StealthRT
fonte

Respostas:

96

É possível, mas requer o uso de SQL dinâmico.
Eu recomendo ler A maldição e as bênçãos do SQL dinâmico antes de continuar ...

DECLARE @theDate varchar(60)
SET @theDate = '''2010-01-01'' AND ''2010-08-31 23:59:59'''

DECLARE @SQL VARCHAR(MAX)  
SET @SQL = 'SELECT AdministratorCode, 
                   SUM(Total) as theTotal, 
                   SUM(WOD.Quantity) as theQty, 
                   AVG(Total) as avgTotal, 
                  (SELECT SUM(tblWOD.Amount)
                     FROM tblWOD
                     JOIN tblWO on tblWOD.OrderID = tblWO.ID
                    WHERE tblWO.Approved = ''1''
                      AND tblWO.AdministratorCode = tblWO.AdministratorCode
                      AND tblWO.OrderDate BETWEEN '+ @theDate +')'

EXEC(@SQL)

SQL dinâmico é apenas uma instrução SQL, composta como uma string antes de ser executada. Portanto, ocorre a concatenação usual de strings. O SQL dinâmico é necessário sempre que você deseja fazer algo na sintaxe SQL que não é permitido, como:

  • um único parâmetro para representar uma lista de valores separados por vírgulas para uma cláusula IN
  • uma variável para representar o valor e a sintaxe SQL (ou seja: o exemplo que você forneceu)

EXEC sp_executesql permite que você use parâmetros de instrução de ligação / preparada para que você não precise se preocupar em escapar de aspas simples / etc para ataques de injeção de SQL.

Pôneis OMG
fonte
Acho que essa é a resposta mais correta. Tenho usado o SQL Server 2005 também recentemente e não é possível usar uma variável para substituição de string de consulta como o OP deseja (gera erros de sintaxe). Variáveis ​​não podem incluir sintaxe e tipos de dados, como diz @Ponies. SQL dinâmico é o caminho a percorrer para construir consultas no SQL Server por meio de strings. Lembre-se de ter cuidado com suas citações e tipos! A string que você executa requer que alguns tipos, como datetime ou int, sejam convertidos ou convertidos para concatenação de string.
RoboBear
52
DECLARE @theDate DATETIME
SET @theDate = '2010-01-01'

Em seguida, altere sua consulta para usar esta lógica:

AND 
(
    tblWO.OrderDate > DATEADD(MILLISECOND, -1, @theDate) 
    AND tblWO.OrderDate < DATEADD(DAY, 1, @theDate)
)
caçador
fonte
2
Espere. Essa não pode ser a resposta se a pergunta mostrar claramente duas datas diferentes. Como você codificou no final @StealthRT? Onde está a data '2010-08-31' na resposta? Além disso, a questão pergunta claramente se você pode usar variáveis ​​DECLARE para substituir o código em outra instrução SELECT. A resposta adequada está abaixo.
Fandango68,
2

Usando EXEC

Você pode usar o seguinte exemplo para construir a instrução SQL.

DECLARE @sqlCommand varchar(1000)
DECLARE @columnList varchar(75)
DECLARE @city varchar(75)
SET @columnList = 'CustomerID, ContactName, City'
SET @city = '''London'''
SET @sqlCommand = 'SELECT ' + @columnList + ' FROM customers WHERE City = ' + @city
EXEC (@sqlCommand)

Usando sp_executesql

Com o uso dessa abordagem, você pode garantir que os valores dos dados passados ​​para a consulta sejam os tipos de dados corretos e evitar o uso de mais aspas.

DECLARE @sqlCommand nvarchar(1000)
DECLARE @columnList varchar(75)
DECLARE @city varchar(75)
SET @columnList = 'CustomerID, ContactName, City'
SET @city = 'London'
SET @sqlCommand = 'SELECT ' + @columnList + ' FROM customers WHERE City = @city'
EXECUTE sp_executesql @sqlCommand, N'@city nvarchar(75)', @city = @city

Referência

Somnath Muluk
fonte
1

Vou apontar que, no artigo com link na resposta com melhor classificação, A maldição e as bênçãos do SQL dinâmico, o autor afirma que a resposta é não usar SQL dinâmico. Role quase até o fim para ver isso.

Do artigo: "O método correto é descompactar a lista em uma tabela com uma função definida pelo usuário ou um procedimento armazenado."

Claro, uma vez que a lista está em uma tabela, você pode usar uma junção. Não pude comentar diretamente sobre a resposta com melhor classificação, então apenas adicionei este comentário.

DavidG
fonte
Isso não fornece uma resposta para a pergunta. Assim que tiver reputação suficiente, você poderá comentar sobre qualquer postagem ; em vez disso, forneça respostas que não exijam esclarecimentos do autor da pergunta . - Da avaliação
Sam M
Obrigado Sam. Atualizarei meu comentário com detalhes assim que implementar o que Erland Sommarskog sugere. Também vou fazer referência a ele pelo nome, já que ele merece crédito pela resposta.
DavidG