web-dev-qa-db-ja.com

カーソルを使用せずにセットに対してストアドプロシージャを実行することは可能ですか?

次のようなカーソルを使用せずに、セット内の各行に対してストアドプロシージャを実行したいと思います。

SELECT EXEC dbo.Sproc @Param1 = Table1.id
FROM Table1


SQL Server 2005でT-SQLを使用しています。これは関数を使用して可能かもしれないと思いますが、可能であればストアドプロシージャを使用したいと思います(会社の標準)

19
user58714

10回のうち9回は、カーソルやwhileループなしでやりたいことができます。ただし、使用する必要がある場合は、whileループの方が高速になる傾向があることがわかりました。

また、テーブルを削除または更新したくない場合は、次のようなものを使用できます。

DECLARE @id [type]
SELECT @id = MIN([id]) FROM [table]
WHILE @id IS NOT NULL
BEGIN
    EXEC [sproc] @id
    SELECT @id = MIN([id]) FROM [table] WHERE [id] > @id
END
0
BankZ

はい。テーブル内で処理済みの列をマークするために使用できる列がある場合は、WHILEEXISTSを使用できます。

DECLARE @Id int
WHILE EXISTS(SELECT * FROM Table1 WHERE Processed='N')
BEGIN
 SELECT Top 1 @Id = id from Table1 WHERE Procesed='N'
 EXEC dbo.Sproc @Id
 UPDATE Table1 SET Processed = 'Y' WHERE Id = @Id
END

または、IDを一時テーブルまたはテーブル変数にダンプし、終了したら削除します。

DECLARE @HoldTable table (Id int PRIMARY KEY)
DECLARE @Id int
INSERT INTO @HoldTable SELECT Id FROM Table1
WHILE EXISTS(SELECT * FROM @HoldTable)
BEGIN
 SELECT @Id = id from @HoldTable
 EXEC dbo.Sproc @Id
 DELETE FROM @HoldTable where Id = @Id
END
18
Josef

SQL Server 2005以降のみを使用していて、下位互換性を気にせず、コードを(ストアドプロシージャではなく)ユーザー定義関数に変換できる場合は、新しい「CROSSAPPLY」演算子を使用できます。 、これはあなたが望むものと非常によく似た構文を使用します。私は見つけました ここ 短いイントロ(もちろん、BOLとMSDNも読むことができます)

SPがout_intという名前の単一の値を返すとすると、例は次のように書き直すことができます。

SELECT T.id, UDF.out_int
FROM 
    Table1 T
CROSS APPLY
    dbo.fn_My_UDF(T.id) AS UDF

これにより、Table1から各「id」が取得され、それを使用してfn_My_UDFが呼び出されます。その結果は、元のパラメーターの他に最終結果セットに表示されます。

「CROSSAPPLY」のバリエーションは「OUTERAPPLY」です。これらは「INNERJOIN」と「LEFTJOIN」に相当しますが、テーブルとUDFを結合する(そして2番目を同時に呼び出す)作業を行います。

must(とがった髪のボスの明示的な順序による)の場合、SPを股下で使用します。カーソルを維持するか、少しごまかしてみる必要があります。コードをUDFに変更し、ラッパーSPを作成します:D。

14
Joe Pineda

率直に言って、データのセットに対してストアドプロシージャを実行する必要がある場合、1つのレコードが書き込まれた場合は、ストアドプロシージャを使用する代わりに、セットベースのコードを記述します。これは、大規模なデータセットに対して実行する場合にこれを行うための唯一の効率的な方法です。ストアドプロシージャが実行する挿入を数時間から数秒または数ミリ秒まで行うことができます。特にコードの再利用などのばかげた理由ではなく、データセットを処理する必要がある場合は、レコードベースの処理を使用しないでください。パフォーマンスは、コードの再利用よりも優先されます。

3
HLGEM

可能であれば、一時テーブルから読み取るストアドプロシージャの2番目のバージョンを作成します。

それが不可能な場合は、おそらく運が悪いでしょう。

1
Joshua

これを見てください answer 動的SQLを使用しています。ニーズに合わせてクエリを書き直します。

DECLARE @TSQL NVARCHAR(MAX) = ''
SELECT @TSQL = @TSQL + N'EXEC dbo.Sproc ' + id + '; 
' -- To insert a line break
FROM Table1
EXEC sp_executesql @TSQL

PRINT @TSQLの場合、これは次のようになります。注 改行の挿入方法

EXEC dbo.Sproc id1; 
EXEC dbo.Sproc id2; 
EXEC dbo.Sproc id3;
...
1
Weihui Guo

このループは機能しますが、私のSPでは遅かったです。正解はHLGEMによるものだと思います。最善の策は、より適切な一括クエリを作成することです。

DECLARE @id INT
SET @id = 0

DECLARE @max INT
SELECT TOP 1 @max = TableID
FROM dbo.Table
ORDER BY TableID desc

-- loop until BREAK
-- this is how you can perform a DO-WHILE loop in TSQL
WHILE (1 = 1) 
BEGIN
    SELECT      
        TOP 1 @id = TableID
        FROM dbo.Table
        WHERE TableID > @id 

    IF @id IS NOT NULL
    BEGIN        
        -- call you SP here
        PRINT @id        
    END

    -- break out of the loop once the max id has been reached
    IF @id = @max BREAK 
END
0
Walter Stabosz