web-dev-qa-db-ja.com

ストアドプロシージャでの動的SQLでのカーソルの使用

ストアドプロシージャで作成した動的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

これを行う正しい方法は何ですか?

46
Micah

カーソルはselectステートメントのみを受け入れるため、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
110
cmsjr

このコードは、@ 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
19
SMHMayboudi

ODBC接続を介した非リレーショナルデータベース(IDMS誰か?).

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)

列Bのinステートメントに1145行が含まれる場合、カーソルを使用して個々のステートメントを作成し、動的SQLとして実行する方が、in句を使用するよりもはるかに高速です。愚かなねえ?

はい、リレーショナルデータベースでは、カーソルを使用する時間はありません。カーソルループが数倍速いインスタンスに出会ったとは信じられません。

4
Twelfth

まず、可能な限りカーソルの使用を避けます。これがなければできないと思われるときに、それを根絶するためのいくつかのリソースがあります:

カーソルを失うには15の方法が必要です...パート1、はじめに

カーソルなしの行ごとの処理

とはいえ、結局のところ、1つにとどまっている可能性があります。これらのいずれかが当てはまることを確認するには、質問から十分に知りません。その場合、別の問題が発生します。カーソルの選択ステートメントは、EXECUTEステートメントではなく、actualSELECTステートメントでなければなりません。あなたが立ち往生しています。

しかし、一時テーブルの使用に関するcmsjr(私が書いている間に入った)からの回答を参照してください。私はglobalカーソルを "プレーン"カーソルよりももっと避けたいです。

3
RolandTumble

最近、OracleからSQL Serverに切り替えた後(雇用者の好み)、SQL Serverでのカーソルサポートが遅れています。カーソルは、常に邪悪であるとは限らず、最適化のヒントを再配置または追加して複雑なクエリを調整しようとするよりも、必要とされることもあります。 「カーソルは悪である」という意見は、SQL Serverコミュニティではるかに顕著です。

したがって、この答えはOracleに切り替えるか、MSに手がかりを与えることだと思います。

1
crokusek

あなたと共有したい別の例があります
:D http://www.sommarskog.se/dynamic_sql.html#cursor

1
SMHMayboudi

SQL Serverのもう1つのオプションは、ストアドプロシージャ内のテーブル変数にすべての動的クエリを実行し、カーソルを使用してクエリと処理を行うことです。恐ろしいカーソルの議論について:)、私はいくつかの状況で、適切に設定すればカーソルが実際により速くなることを示す研究を見てきました。必要なクエリが複雑すぎる場合、または人間にとって(私にとっては;))不可能な場合に自分で使用します。

0
davaus