Использование курсора с динамическим SQL в хранимой процедуре

StackOverflow https://stackoverflow.com/questions/1045880

Вопрос

У меня есть динамический оператор 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

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

Это было полезно?

Решение

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

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

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

Этот код является очень хорошим примером для динамического столбца с курсором, поскольку вы не можете использовать '+' в @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

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

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

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

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

Построчная обработка без использования курсора

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

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

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

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

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

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

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

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

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 
      ...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top