Как передать строку в динамический SQL внутри хранимой процедуры - PullRequest
0 голосов
/ 06 июля 2018

На данный момент у меня есть SQL-выражение вроде этого:

SELECT 
    y.ID, y.STATUS, y.CONTROLID, y.TRCDE
FROM 
    YTDTRNI AS y 
LEFT JOIN 
    VND AS v ON y.COMNO = v.COMNO 
WHERE 
    TRCDE ='RC' 
ORDER BY 
    ID DESC

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

SET @query =  N'SELECT y.ID, y.STATUS, y.CONTROLID, y.TRCDE
                FROM YTDTRNI AS y LEFT JOIN VND AS v ON y.COMNO = v.COMNO 
                WHERE TRCDE = ' + @searchtrtype + ' 
                ORDER BY ' + @orderbycondition

Переменные @searchtrtype и @orderbycondition имеют тип nvarchar.

Я использую программу ASP.NET/C# для вызова хранимой процедуры. Тем не менее, он ломается с исключением:

Выражение не булева типа в контексте, где условие ожидается около 'ORDER'

Мне кажется, что я получаю ошибку, потому что строковые значения неправильно объединены или отформатированы внутри переменной @query.

Открыт для любых советов.

РЕДАКТИРОВАТЬ: моя хранимая процедура выглядит следующим образом:

enter image description here

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

Необходимо объявить скалярную переменную "@dsearchtrtype".

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

Ответы [ 4 ]

0 голосов
/ 06 июля 2018

На основании предоставленного примера представляется, что OP просто хочет отфильтровать один столбец и выполнить сортировку по указанному пользователем столбцу. Этого можно достичь с помощью параметризованного запроса без необходимости динамического SQL.

В предложении where поле TRCDE может быть непосредственно присвоено параметру @searchtrtype. Требование динамической сортировки немного сложнее, но может быть достигнуто с помощью оператора CASE в предложении ORDER BY. Дополнительный параметр должен быть включен, чтобы указать, должна ли сортировка быть возрастающей или убывающей. Следующий запрос демонстрирует:

DECLARE @orderByColumn  NVARCHAR(50) = 'ID'; -- The column to sort by
DECLARE @sortAscending BIT = 1; -- Indicate sorting order 1 == Ascending, 0 == Descending
DECLARE @searchtrtype NVARCHAR(10) = 'RC'

IF @sortAscending = 1 -- Sort data in ascending order
BEGIN

    SELECT       y.[ID]
                ,y.[STATUS]
                ,y.[CONTROLID]
                ,y.[TRCDE]
    FROM        YTDTRNI AS y 
    LEFT JOIN   VND AS v ON y.[COMNO] = v.[COMNO]
    WHERE       TRCDE = @searchtrtype 
    ORDER BY    (
                    -- Create case statement for every column user may want to filter on
                    CASE
                        WHEN @orderByColumn = 'ID' THEN [ID]
                        ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
                    END
                 ),
                 (
                    CASE
                        WHEN @orderByColumn = 'STATUS' THEN [STATUS]
                        ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
                    END
                 ),
                             (
                    CASE
                        WHEN @orderByColumn = 'CONTROLID' THEN [CONTROLID]
                        ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
                    END
                 ) ASC;
END
ELSE
BEGIN
    SELECT       y.[ID]
                ,y.[STATUS]
                ,y.[CONTROLID]
                ,y.[TRCDE]
    FROM        YTDTRNI AS y 
    LEFT JOIN   VND AS v ON y.[COMNO] = v.[COMNO]
    WHERE       TRCDE = @searchtrtype 
    ORDER BY    (
                    CASE
                        WHEN @orderByColumn = 'ID' THEN [ID]
                        ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
                    END
                 ),
                 (
                    CASE
                        WHEN @orderByColumn = 'STATUS' THEN [STATUS]
                        ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
                    END
                 ),
                             (
                    CASE
                        WHEN @orderByColumn = 'CONTROLID' THEN [CONTROLID]
                        ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
                    END
                 ) DESC;
END

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

0 голосов
/ 06 июля 2018

Научитесь использовать параметры с sp_executesql. Вы можете сделать это для @searchtrtype, но не @orderbycondition:

SET @query =  N'
SELECT y.ID, y.STATUS,y.CONTROLID,y.TRCDE
FROM YTDTRNI y LEFT JOIN
     VND v
     ON y.COMNO = v.COMNO 
WHERE TRCDE = @searchtrtype 
ORDER BY @orderbycondition';

SET @query = REPLACE(@query, '@orderbycondition', @orderbycondition);

EXEC sp_executesql @query,
                   N'@searchtrtype NVARCHAR(255)',   -- or whatever the right type is
                   @searchtrtype=@searchtrtype;

Вы не можете передавать идентификаторы, только значения, поэтому это не работает для @orderbycondition.

0 голосов
/ 06 июля 2018

Вам нужно параметризировать ваш SQL. Конкатенация строки для SQL - ужасная идея; как это оставляет вас открытым для инъекций. То, как вы хотите это сделать, будет:

DECLARE @query nvarchar(MAX);
SET @query =  N'SELECT y.ID,' + NCHAR(10) + 
              N'       y.STATUS,' + NCHAR(10) + 
              N'       y.CONTROLID,' + NCHAR(10) + 
              N'       y.TRCDE' + NCHAR(10) + 
              N'FROM YTDTRNI AS y' + NCHAR(10) + 
              N'     LEFT JOIN VND AS v ON y.COMNO = v.COMNO ' + NCHAR(10) + 
              N'WHERE TRCDE = @dsearchtrtype' + NCHAR(10) + 
              N'ORDER BY ' + QUOTENAME(@orderbycondition) + N';';
PRINT @SQL;

EXEC sp_executesql @query, N'@dsearchtrtype nvarchar(100)', @dsearchtrtype = @searchtrtype;

Однако, вероятно, это не сработает из-за вашей переменной @orderbycondition. Я не знаю, однако, какой тип значения имеет, однако, если это что-то вроде 'ID desc', это станет ORDER BY [ID desc];.

Если это предположение верно, я бы предложил использовать 2 переменные; один для столбца сортировки и один для направления, и замените последнюю строку динамического SQl на:

N'ORDER BY ' + QUOTENAME(@orderbycolumn) + N' ' + CASE WHEN @orderbydirection NOT IN(N'ASC',N'DESC') THEN N'' ELSE @orderbydirection END + N';';

Если ваше значение @orderbycondition может быть более сложным, отправьте комментарий, чтобы сообщить мне (и обновите ваш вопрос более подробно), и я с удовольствием объясню, как создать более динамичный ORDER BY предложение с использованием параметра типа таблицы, а также добавление его в динамический SQL с использованием STUFF и FOR XML PATH.

0 голосов
/ 06 июля 2018

вам просто нужно включить экранированные апострофы. если @searchtrtype может содержать апострофы, вам, возможно, придется избегать их с помощью REPLACE (@searchtrtype, '' '', '' '' '')

SET @query =  N'SELECT y.ID, y.STATUS,y.CONTROLID,y.TRCDE
FROM YTDTRNI AS y LEFT JOIN VND AS v ON y.COMNO = v.COMNO 
WHERE TRCDE = ''' + @searchtrtype + ''' 
ORDER BY ' + @orderbycondition
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...