Использование курсора с динамическим SQL в хранимой процедуре - PullRequest
45 голосов
/ 25 июня 2009

У меня есть динамический оператор SQL, который я создал в хранимой процедуре. Мне нужно перебрать результаты с помощью курсора. Мне трудно понять правильный синтаксис. Вот что я делаю.

SELECT @SQLStatement = 'SELECT userId FROM users'

DECLARE @UserId

DECLARE users_cursor CURSOR FOR
EXECUTE @SQLStatment --Fails here. Doesn't like this

OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN

EXEC asp_DoSomethingStoredProc @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

Как правильно это сделать?

Ответы [ 8 ]

110 голосов
/ 26 июня 2009

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

Declare @UserID varchar(100)
declare @sqlstatement nvarchar(4000)
--move declare cursor into sql to be executed
set @sqlstatement = 'Declare  users_cursor CURSOR FOR SELECT userId FROM users'

exec sp_executesql @sqlstatement


OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN
Print @UserID
EXEC asp_DoSomethingStoredProc @UserId

FETCH NEXT FROM users_cursor --have to fetch again within loop
INTO @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

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

Declare @UserID varchar(100)
create table #users (UserID varchar(100))

declare @sqlstatement nvarchar(4000)
set @sqlstatement = 'Insert into #users (userID) SELECT userId FROM users'
exec(@sqlstatement)

declare users_cursor cursor for Select UserId from #Users
OPEN users_cursor
FETCH NEXT FROM users_cursor
INTO @UserId

WHILE @@FETCH_STATUS = 0
BEGIN

EXEC asp_DoSomethingStoredProc @UserId

FETCH NEXT FROM users_cursor
INTO @UserId

END
CLOSE users_cursor
DEALLOCATE users_cursor

drop table #users
18 голосов
/ 07 июля 2011

Этот код является очень хорошим примером для динамического столбца с курсором, так как вы не можете использовать '+' в @STATEMENT:

ALTER PROCEDURE dbo.spTEST
AS
    SET NOCOUNT ON
    DECLARE @query NVARCHAR(4000) = N'' --DATA FILTER
    DECLARE @inputList NVARCHAR(4000) = ''
    DECLARE @field sysname = '' --COLUMN NAME
    DECLARE @my_cur CURSOR
    EXECUTE SP_EXECUTESQL
        N'SET @my_cur = CURSOR FAST_FORWARD FOR
            SELECT
                CASE @field
                    WHEN ''fn'' then fn
                    WHEN ''n_family_name'' then n_family_name
                END
            FROM
                dbo.vCard
            WHERE
                CASE @field
                    WHEN ''fn'' then fn
                    WHEN ''n_family_name'' then n_family_name
                END
                LIKE ''%''+@query+''%'';
            OPEN @my_cur;',
        N'@field sysname, @query NVARCHAR(4000), @my_cur CURSOR OUTPUT',
        @field = @field,
        @query = @query,
        @my_cur = @my_cur OUTPUT
    FETCH NEXT FROM @my_cur INTO @inputList
    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT @inputList
        FETCH NEXT FROM @my_cur INTO @inputList
    END
    RETURN
4 голосов
/ 23 сентября 2010

Работа с нереляционной базой данных (IDMS кто-нибудь?) Через соединение ODBC квалифицируется как один из тех случаев, когда курсоры и динамический SQL кажутся единственным маршрутом.

select * from a where a=1 and b in (1,2)
Для ответа

требуется 45 минут, в то время как перезапись для использования наборов ключей без условия in будет выполняться менее чем за 1 секунду:

select * from a where (a=1 and b=1)
union all
select * from a where (a=1 and b=2)

Если оператор in для столбца B содержит 1145 строк, использование курсора для создания операторов indidivudal и их выполнения в качестве динамического SQL намного быстрее, чем при использовании предложения in. Глупый, эй?

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

3 голосов
/ 26 июня 2009

Прежде всего, избегайте использования курсора, если это вообще возможно. Вот некоторые ресурсы для того, чтобы искоренить это, когда кажется, что вы не можете обойтись без:

Должно быть 15 способов потерять свои курсоры ... часть 1, Введение

Строковая обработка без курсора

Тем не менее, вы, возможно, застряли с одним из них - я не знаю достаточно из вашего вопроса, чтобы быть уверенным, что любой из них применим. В этом случае у вас другая проблема - оператор выбора для вашего курсора должен быть оператором фактическим SELECT, а не оператором EXECUTE. Вы застряли.

Но посмотрите ответ от cmsjr (который пришел, пока я писал) об использовании временной таблицы. Я бы избегал глобальных курсоров даже больше, чем "простые" ....

1 голос
/ 07 июля 2011

Есть еще один пример, которым я хотел бы поделиться с вами
: D http://www.sommarskog.se/dynamic_sql.html#cursor0

1 голос
/ 24 мая 2011

После недавнего перехода с Oracle на SQL Server (предпочтение работодателя) я заметил, что поддержка курсоров в SQL Server отстает. Курсоры не всегда злые, иногда требуются, иногда гораздо быстрее, а иногда и чище, чем попытки настроить сложный запрос путем реорганизации или добавления подсказок по оптимизации. Мнение «курсоры - зло» гораздо более заметно в сообществе SQL Server.

Так что я думаю, что этот ответ - переключиться на Oracle или дать MS ключ.

0 голосов
/ 10 мая 2019

Другой вариант в SQL Server - выполнить все ваши динамические запросы в табличную переменную в хранимом процессе, а затем использовать курсор для запроса и обработки этого. Что касается спорных вопросов о курсоре :), я видел исследования, которые показывают, что в некоторых ситуациях курсор может быть быстрее при правильной настройке. Я использую их сам, когда требуемый запрос слишком сложен или просто не по-человечески (для меня;)) возможен.

0 голосов
/ 30 ноября 2012

этот код может быть полезен для вас.

пример использования курсора на сервере sql

DECLARE sampleCursor CURSOR FOR 
      SELECT K.Id FROM TableA K WHERE ....;
OPEN sampleCursor
FETCH NEXT FROM sampleCursor INTO @Id
WHILE @@FETCH_STATUS <> -1
BEGIN

UPDATE TableB
   SET 
      ...
...