Синтаксическая ошибка: динамический SQL в хранимой процедуре

Я новичок в SQL Server, что может быть причиной этой синтаксической ошибки.

Мой код:

CREATE PROCEDURE spBaseVoterIndex  
    @order_col NVARCHAR(100) ,  
    @order_dir NVARCHAR(20) ,  
    @offset ITN,  
    @limit INT 
AS
    SET NOCOUNT ON;  
BEGIN
    DECLARE @sql NVARCHAR(MAX)
    SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter'  
               +' WITH(NOLOCK)'  
               +' ORDER BY @OC @OD'  
               +' OFFSET @OF ROWS'  
               +' FETCH NEXT @LIM ROWS ONLY'  

    EXECUTE sp_executesql @sql,  
               N'@OC nvarchar(191),@OD nvarchar(10),@OF int,@LIM int',  
               @OC @order_col, @OD @order_dir, @OF @offset, @LIM @limit  
END

Ошибка:

Msg 102, Level 15, State 1, Procedure spBaseVoterIndex, Line 18 [Batch Start Line 0]
Incorrect syntax near '@order_col'.

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

Печать @SQL приводит к

SELECT id, name_voter, home_street_address_1, home_address_city 
FROM dbo.base_voter WITH(NOLOCK) 
ORDER BY @OC @OD 
OFFSET @OF ROWS 
FETCH NEXT @LIM ROWS ONLY

Обновлять

У меня этот код работает

SELECT id,
name_voter,
home_street_address_1,
home_address_city
FROM dbo.base_voter
WITH(NOLOCK)
WHERE deleted_at IS NULL
order by name_voter asc
OFFSET 0 ROWS 
FETCH NEXT 50 ROWS ONLY 

Все, что я хочу сделать динамическим, и среди них эти name_voter asc и значение смещения 0 и предельное значение 50, это 4 параметра, которые я хочу сделать динамическими.

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

Ваша одинарная кавычка находится в первой строке только в части "Выполнить"

slon 10.10.2018 06:02

Первое, что вы делаете неправильно, - это не распечатываете и не проверяете @sql. Это ваша первая остановка.

Nick.McDermaid 10.10.2018 06:10

@slon извини, я не понимаю

user7171595 10.10.2018 06:11

Посмотрите цвет последних двух строк кода прямо перед концом

slon 10.10.2018 06:12

@nick Printing @sql показывает SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter WITH(NOLOCK) ORDER BY @OC @OD OFFSET @OF ROWS FETCH NEXT @LIM ROWS ONLY

user7171595 10.10.2018 06:12

Я отредактирую ваш вопрос и вставлю его - в будущем, пожалуйста, сделайте это так, поскольку тогда в вопросе будет вся информация.

Nick.McDermaid 10.10.2018 06:24

Хорошо, похоже, что в вашем SQL есть некоторые синтаксические ошибки, но есть также ошибки в вашем sp_executesql , как уже указывалось.

Nick.McDermaid 10.10.2018 06:25

@slon Я обновил вторую строку выполнения с помощью N'@OC @order_col,@OD @order_dir,@OF @offset,@LIM @limit'. Хотя он строит хранимую процедуру. Однако, если я попытаюсь выполнить spBaseVoterIndex 'name_voter', 'asc', 0, 50, он покажет ошибку Msg 102, Level 15, State 1, Line 22 Incorrect syntax near '@OD'. Msg 153, Level 15, State 2, Line 22 Invalid usage of the option NEXT in the FETCH statement.

user7171595 10.10.2018 06:27

Если вы проверите свой код вокруг идентификатора @order_col, как указано в ошибке, вы увидите, что отсутствует запятая. Но я не думаю, что это все равно сработает - я не думаю, что вы можете параметризовать это так

Nick.McDermaid 10.10.2018 06:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
617
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Похоже, вам не хватает запятой между @OC и @OD в строке + 'ЗАКАЗАТЬ @OC @OD'

Вы также заметили, что размер ваших параметров меняется между определением процедуры и определением динамического SQL?

Вы можете протестировать, заменив динамические параметры SQL фактическими и просто распечатав последнюю строку, а затем запустив ее.

Помимо того, что вы знаете о возможных проблемах с использованием NOLOCK (грязное чтение, фантомное чтение, дублированные данные), ваше использование выглядит допустимым.

Это необработанный запрос, который работает, SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter WITH(NOLOCK) ORDER BY name_voter asc OFFSET 0 ROWS FETCH NEXT 50 ONLY. И вы говорите, что я должен поставить запятую между name_voter и asc. Я не думаю, что это имеет смысл.

user7171595 10.10.2018 06:42

Хорошо, это не сработает. Вам нужно использовать динамический SQL, что означает, что вы объединяете переменные в строку. Вы не передаете их в sp_executesql как параметры.

Nick.McDermaid 10.10.2018 09:10

Попробуй это.

CREATE PROCEDURE spBaseVoterIndex  
    @order_col NVARCHAR(100) ,  
    @order_dir NVARCHAR(20) ,  
    @offset INT,  
    @limit INT 
AS
    SET NOCOUNT ON;  
BEGIN
    DECLARE @sql NVARCHAR(MAX)
    SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter'  
               +' WITH(NOLOCK)'  
               +' ORDER BY @OC @OD'  
               +' OFFSET @OF ROWS'  
               +' FETCH NEXT @LIM ROWS ONLY'  

    EXECUTE sp_executesql @sql,  
               N'@OC nvarchar(100),@OD nvarchar(20),@OF int,@LIM int',  
               @OC=@order_col, @OD=@order_dir, @OF=@offset, @LIM=@limit  
END

Что ж, это выполняется без ошибок. Но когда я пытаюсь выполнить spBaseVoterIndex 'name_voter','asc',0,50, выдает ошибку Msg 102, Level 15, State 1, Line 21 Incorrect syntax near '@OD'. Msg 153, Level 15, State 2, Line 21 Invalid usage of the option NEXT in the FETCH statement.

user7171595 10.10.2018 06:31

Попробуйте добавить запятую между @OD и @OD в предложении ORDER BY.

AswinRajaram 10.10.2018 06:38

Это необработанный запрос, который работает, SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter WITH(NOLOCK) ORDER BY name_voter asc OFFSET 0 ROWS FETCH NEXT 50 ONLY. И вы говорите, что я должен поставить запятую между name_voter и asc. Я не думаю, что это имеет смысл.

user7171595 10.10.2018 06:42

Я думал, что переменная @OD обозначает другой столбец. Запрос кажется нормальным и должен работать.

AswinRajaram 10.10.2018 06:46

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

CREATE PROCEDURE spBaseVoterIndex  
    @order_col NVARCHAR(191) ,  
    @order_dir NVARCHAR(10) ,  
    @offset INT,  
    @limit INT 
AS
    SET NOCOUNT ON;  
BEGIN
    DECLARE @sql NVARCHAR(MAX)

    SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter'  
            +' WITH(NOLOCK)'  
            +' ORDER BY @order_col @order_dir'  
            +' OFFSET @offset ROWS'  
            +' FETCH NEXT @limit ROWS ONLY' 

    SET @sql = REPLACE(@sql,'@order_col',@order_col)
    SET @sql = REPLACE(@sql,'@order_dir',@order_dir)
    SET @sql = REPLACE(@sql,'@offset',@offset)
    SET @sql = REPLACE(@sql,'@limit',@limit)

    EXECUTE sp_executesql @sql

END

Кстати, в нединамическом SQL OFFSET и FETCH NEXT принимают переменные INT. Итак, вы можете, например, использовать его (напрямую) следующим образом:

SELECT 
    id
,   name_voter
,   home_street_address_1
,   home_address_city 
FROM 
    dbo.base_voter
ORDER BY id
OFFSET @offset ROWS
FETCH NEXT @limit ROWS ONLY

Но это заставит вас использовать фиксированный порядок (что, кстати, не является реальной проблемой). Если вам нужно использовать порядок по, вы можете использовать метод ROW_NUMBER, примерно так:

SELECT * FROM (
SELECT 
    id
,   name_voter
,   home_street_address_1
,   home_address_city 
,   ROW_NUMBER() OVER(ORDER BY @order_col + @order_dir) RN
FROM 
    base_voter
) D 
WHERE
    RN > @Offset
AND RN <= @Limit + @Offset

это даст вам те же результаты, что и OFFSET и FETCH NEXT

Надеюсь, когда-нибудь это будет полезно.

Это возвращает ошибку Msg 102, Level 15, State 1, Line 31 Incorrect syntax near '@OD'. Msg 153, Level 15, State 2, Line 31 Invalid usage of the option NEXT in the FETCH statement., когда я выполняю spBaseVoterIndex 'name_voter','asc',0,50

user7171595 10.10.2018 07:31
Ответ принят как подходящий

Это не работает - вы не можете параметризовать ASC таким образом. Вам нужно буквально соединить строки.

Хорошая идея - добавить санацию строк, чтобы остановить SQL-инъекцию.

CREATE PROCEDURE spBaseVoterIndex  
    @order_col NVARCHAR(100) ,  
    @descending INT,  
    @offset INT,  
    @limit INT 
AS
    SET NOCOUNT ON;  
BEGIN

   DECLARE @sql NVARCHAR(MAX)
   DECLARE @OD varchar(4) = 'ASC'

   -- Default is ascending. Pass in 1 to order descending
   IF @descending = 1 THEN SET @OD='DESC'

   -- If the order column is not valid, exit
   -- You need to put a list of valid columns in here
   IF @order_col NOT IN ('column1','column2','column3') RETURN


   -- Finally build and run the SQL
    SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city'
               +' FROM dbo.base_voter'  
               +' WITH(NOLOCK)'  
               +' ORDER BY ' + @order_col + ' ' + @OD + ' '
               +' OFFSET ' + CAST(@offset AS VARCHAR(20)) + ' ROWS'  
               +' FETCH NEXT ' + CAST(@limit AS VARCHAR(20)) + ' ROWS ONLY'  

EXEC(@sql)

END

Что касается производительности, здесь есть две части:

  1. Убедитесь, что у вас правильные индексы
  2. Этот тип динамического SQL-запроса "кухонной раковины" всегда может иметь проблемы с производительностью, независимо от того, что вы не можете предварительно скомпилировать план запроса. Однако он никогда не пострадает от обнюхивания параметров.

Разве это не будет подвержено SQL-инъекции?

user7171595 10.10.2018 09:25

Определенно

Nick.McDermaid 10.10.2018 09:34

не могли бы вы подсказать, как мне поступить с описанием дела

user7171595 10.10.2018 09:46

Я только что обновил вопрос, пожалуйста, посмотрите.

user7171595 10.10.2018 10:07

Другие вопросы по теме