2011-12-22 3 views
0

Я пытаюсь создать хранимую процедуру, которая использует переменное количество параметров. Поскольку я довольно новичок в написании хранимых процедур и TSQL в целом, я решил попробовать и написать его только с одним параметром. Тем не менее, я продолжаю получать сообщение об ошибке «Должен объявить скалярную переменную @FirstName» при попытке выполнить ее. Прямо сейчас я пытаюсь сохранить инструкцию SQL в другой переменной @sql. Моя процедура выглядит следующим образом:Ошибка при объявлении параметра в хранимой процедуре

ALTER PROCEDURE [dbo].[GetEmployeeByParameters] 
(@FirstName varchar(50)) 

AS 

BEGIN 

    DECLARE @sql AS NVARCHAR(4000) 
    SET @sql = 'SELECT e.* from Employee e 
       WHERE e.FirstName = @FirstName' 
    EXEC (@sql) 
END 

Я посмотрел в другом месте и попытался EXEC sp_execute @sql, который не работал. Как ни странно, что работает, когда я не объявляю переменную @sql и вместо этого просто пишу свой запрос нормально. Поскольку это так, я предполагаю, что есть некоторая ошибка в моем использовании функций SET и EXEC. Я также не уверен на 100%, что я правильно использую BEGIN и END. Я понял, что BEGIN и END разделяют SQL-запросы на логические блоки и, следовательно, чаще используются, когда IF входит в игру. Может ли кто-нибудь сказать мне, что именно происходит с моим параметром здесь? Меня действительно путают, почему SQL Server считает, что он не объявлен.

ответ

3

Variabl e должен находиться вне кавычек.

SET @sql = N'SELECT e.* from Employee e 
      WHERE e.FirstName = ''' + @FirstName + '''' 

Или, еще лучше, запустите его без динамического SQL.

SELECT e.* 
    from Employee e 
    WHERE e.FirstName = @FirstName 
+0

Спасибо, Джо, это сработало как шарм! Если я правильно понял, переменный параметр действительно должен быть внутри кавычек, но просто не является частью строки @ sql? Это единственная причина, по которой я могу думать о тройных и четырехкратных одинарных кавычках, окружающих @ FirstName – Zajn

+1

+1, для этого не используйте динамический SQl. – HLGEM

1

Поскольку

'Select ... @FirstName' 

не то же самое, как

Select ... @FirstName 

One является строка, а другой является SQL Query

Что вы должны сделать вместо этого

ALTER PROCEDURE [dbo].[GetEmployeeByParameters] 
(@FirstName varchar(50)) 

AS 

BEGIN 

    DECLARE @sql AS NVARCHAR(4000) 
    SET @sql = 'SELECT e.* from Employee e 
       WHERE e.FirstName = ''' + @FirstName + '''' 
    EXEC (@sql) 
END 
+0

Спасибо за прояснение разницы между фактическим запросом и строкой; что помогает! – Zajn

0

Вы должны изменить свой запрос на следующее в качестве переменной @Firstname не в объеме:

ALTER PROCEDURE [dbo].[GetEmployeeByParameters] 
(@FirstName varchar(50)) 

AS 

BEGIN 

    DECLARE @sql AS NVARCHAR(4000) 
    SET @sql = 'SELECT e.* from Employee e 
       WHERE e.FirstName = ''' + @FirstName + '''' 
    EXEC (@sql) 
END 
0

Используйте это тело, без "динамический" запрос

ALTER PROCEDURE [dbo].[GetEmployeeByParameters] 
(@FirstName varchar(50)) 

AS 

BEGIN 

    SELECT e.* from Employee e 
       WHERE e.FirstName = @FirstName 
END 

OR

с использованием динамического запроса не включает variabl в его скрипте - соедините их со сценарием и не забудьте правильно процитировать их.

+0

Не будет работать, если он добавит переменное количество параметров. –

+0

@Chris J - Thanx для заметки, немного поправил ответ –

1

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

Очевидно, что вам не нужно динамический SQL в этом случае, но как только метод делает это так:

ALTER PROCEDURE [dbo].[GetEmployeeByParameters] 
    (@FirstName varchar(50))  
AS 
     BEGIN 
      DECLARE @sql AS NVARCHAR(4000) 
     SET @sql = 'SELECT e.* from Employee e 
        WHERE e.FirstName = @FirstName' 
     EXEC sp_executeSQL @sql, N'@Firstname varchar(50)', @FirstName 
    END 

sp_executesql позволяет объявлять внутренние параметры (второе предложение), а также снабжать их со значениями (последнее предложение).

0

Поскольку это запрос типа поиска, который вы делаете с переменным числом параметров, вам нужно построить строку по-разному - вы на правильных строках, чтобы она была динамичной, но вы также необходимо избегать инъекций SQL-инъекций (google «Little Bobby Tables»). Таким образом, вы должны использовать параметризованный динамический оператор SQL:

ALTER PROCEDURE [dbo].[GetEmployeeByParameters] 
    @FirstName VARCHAR(50) 
AS 
BEGIN 
    DECLARE @sql AS NVARCHAR(4000) 

    SET @sql = 'SELECT e.* FROM Employee e WHERE 1 = 1' 
    IF @FirstName IS NOT NULL 
    BEGIN 
     SET @sql = @sql + ' AND FirstName = @pFirstName' 
    END 
    -- More IF statements, building up the query 

    EXEC sp_ExecuteSQL @sql, N'@pFirstName VARCHAR(50)', @FirstName 

Второй и третий PARAMS картированию параметра @FirstName для «внутренних» параметров в запросе (который я обычно префикс с «р» или «парами» просто чтобы отличить их от собственных параметров хранимой процедуры).

Вы продлеваете sp_Exceute по мере необходимости каждый раз, когда вы добавляете новый параметр для поиска по, так что вы могли бы в конечном итоге делаете:

EXEC sp_ExecuteSQL @sql,' N' 
       @pFirstName VARCHAR(50), 
       @pSurName  VARCHAR(50), 
       @pDateOfBirth DATETIME', @FirstName, @Surname, @DateOfBirth 
+0

Крис, это именно то, о чем я думал. Я пытался сделать что-то подобное раньше с инструкциями IF NOT NULL, но я продолжал сталкиваться с этой проблемой «Должен объявить скалярную переменную», которая теперь очищается. Спасибо за объяснение на sp_ExecuteSQL! Я бы использовал BEGIN ... END внутри каждого оператора IF в этом случае? – Zajn

+0

Вам не нужно - я делаю это по привычке дезинвестиционного программирования, поскольку он делает более понятным (когда дело доходит до обслуживания), где IF начинается и заканчивается, если другой разработчик меняет отступы или что-то в этом роде. Один (из многих) обсуждений по этой теме можно найти по адресу http://c2.com/cgi/wiki?AlwaysUseBracesOnIfThen (речь идет о фигурных скобках в соответствии с C, Java, C# и т. Д., Но те же аргументы применимы и здесь). –

Смежные вопросы