Объявить переменную для строки запроса

92

Мне было интересно, есть ли способ сделать это в 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

Возможно ли это сделать?

StealthRT
источник

Ответы:

97

Это возможно, но требует использования динамического SQL.
Я рекомендую прочитать Проклятие и благословение динамического SQL, прежде чем продолжить ...

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 - это просто оператор SQL, составленный в виде строки перед выполнением. Так происходит обычная конкатенация строк. Динамический SQL требуется всякий раз, когда вы хотите сделать что-то недопустимое в синтаксисе SQL, например:

  • один параметр для представления списка значений, разделенных запятыми, для предложения IN
  • переменная, представляющая как значение, так и синтаксис SQL (IE: пример, который вы предоставили)

EXEC sp_executesql позволяет использовать параметры bind / preparestatement, поэтому вам не нужно беспокоиться об экранировании одинарных кавычек / etc для атак с использованием SQL-инъекций.

OMG Пони
источник
Думаю, это самый правильный ответ. Я также недавно использовал SQL Server 2005, и использование переменной для замены строки запроса, как того требует OP, невозможно (генерирует синтаксические ошибки). Как говорит @Ponies, переменные не могут включать одновременно синтаксис и типы данных. Динамический SQL - это способ построения запросов в SQL Server с помощью строк. Только не забудьте быть осторожными с цитатами и типами! Строка, которую вы выполняете, требует преобразования или преобразования некоторых типов, таких как datetime или int, для конкатенации строк.
RoboBear
52
DECLARE @theDate DATETIME
SET @theDate = '2010-01-01'

Затем измените свой запрос, чтобы использовать эту логику:

AND 
(
    tblWO.OrderDate > DATEADD(MILLISECOND, -1, @theDate) 
    AND tblWO.OrderDate < DATEADD(DAY, 1, @theDate)
)
охотник
источник
2
Подожди. Это не может быть ответом, если в вопросе четко указаны две разные даты. Как вы его закодировали в конце @StealthRT? Где в ответе дата «2010-08-31»? Кроме того, вопрос четко задает вопрос, можно ли использовать переменные DECLARE для замены кода в другой оператор SELECT. Правильный ответ ниже.
Fandango68,
2

Использование EXEC

Вы можете использовать следующий пример для построения оператора 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)

Использование sp_executesql

Используя этот подход, вы можете гарантировать, что значения данных, передаваемые в запрос, являются правильными типами данных, и избежать использования дополнительных кавычек.

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

Справка

Сомнатх Мулук
источник
1

Я отмечу, что в статье, связанной с самым популярным ответом «Проклятие и благословения динамического SQL», автор заявляет, что ответ - не использовать динамический SQL. Прокрутите почти до конца, чтобы увидеть это.

Из статьи: «Правильный метод - распаковать список в таблицу с пользовательской функцией или хранимой процедурой».

Конечно, когда список находится в таблице, вы можете использовать соединение. Я не мог напрямую комментировать самый популярный ответ, поэтому просто добавил этот комментарий.

DavidG
источник
Это не дает ответа на вопрос. Как только у вас будет достаточная репутация, вы сможете комментировать любой пост ; вместо этого предоставьте ответы, которые не требуют пояснений от спрашивающего . - Из отзыва
Sam M
Спасибо, Сэм. Я дополню свой комментарий подробностями, как только выполню то, что предлагает Эрланд Соммарског. Я также назову его по имени, так как он заслуживает уважения за ответ.
DavidG 08