ユーザーデータを特定の方法で変更するストアドプロシージャがあります。user_idを渡します。テーブルでクエリを実行したいのですが、user_idごとに、そのuser_idでストアドプロシージャを1回実行します。
これに対するクエリをどのように記述しますか?
ユーザーデータを特定の方法で変更するストアドプロシージャがあります。user_idを渡します。テーブルでクエリを実行したいのですが、user_idごとに、そのuser_idでストアドプロシージャを1回実行します。
これに対するクエリをどのように記述しますか?
回答:
カーソルを使用する
補遺:[MS SQLカーソルの例]
declare @field1 int
declare @field2 int
declare cur CURSOR LOCAL for
select field1, field2 from sometable where someotherfield is null
open cur
fetch next from cur into @field1, @field2
while @@FETCH_STATUS = 0 BEGIN
--execute your sproc on each row
exec uspYourSproc @field1, @field2
fetch next from cur into @field1, @field2
END
close cur
deallocate cur
MS SQLでは、ここに例の記事があります
カーソルはセットベースの操作よりも低速ですが、手動のwhileループよりは高速です。このSO質問の詳細
補遺2:数個以上のレコードを処理する場合は、まずそれらを一時テーブルにプルし、一時テーブルにカーソルを合わせます。これにより、SQLがテーブルロックにエスカレートするのを防ぎ、操作を高速化します
補遺3:そしてもちろん、ストアドプロシージャが各ユーザーIDに対して実行していることをすべてインライン化し、すべてを単一のSQL更新ステートメントとして実行できる場合、それが最適です。
ループする必要がある場合は、メソッドを変更してみてください!
親ストアドプロシージャ内で、処理する必要のあるデータを含む#tempテーブルを作成します。子ストアドプロシージャを呼び出すと、#tempテーブルが表示され、カーソルやループなしでデータセット全体を処理できるようにテーブルを処理できます。
これは実際には、この子のストアドプロシージャが何をしているかによって異なります。UPDATEを実行している場合は、#tempテーブルに「から更新」して結合し、ループなしで1つのステートメントですべての作業を実行できます。INSERTとDELETEについても同じことができます。IFを使用して複数の更新を行う必要がある場合UPDATE FROM
は、#tempテーブルを使用して複数に更新し、CASEステートメントまたはWHERE条件を使用できます。
データベースで作業しているときにループの考え方を失おうとすると、それは実際のパフォーマンスの低下であり、ロック/ブロックを引き起こし、処理を遅くします。どこでもループすると、システムのスケーリングがうまくいかなくなり、ユーザーがリフレッシュが遅いと不満を感じ始めたときにスピードアップが非常に難しくなります。
ループで呼び出したいこのプロシージャのコンテンツを投稿してください。10回のうち9回は賭けるので、一連の行を処理するように記述できます。
この置換のようなものがテーブルとフィールド名に必要になります。
Declare @TableUsers Table (User_ID, MyRowCount Int Identity(1,1)
Declare @i Int, @MaxI Int, @UserID nVarchar(50)
Insert into @TableUser
Select User_ID
From Users
Where (My Criteria)
Select @MaxI = @@RowCount, @i = 1
While @i <= @MaxI
Begin
Select @UserID = UserID from @TableUsers Where MyRowCount = @i
Exec prMyStoredProc @UserID
Select
@i = @i + 1, @UserID = null
End
これは、ストアドプロシージャが実行していることをすべて複製するユーザー定義関数では実行できませんか?
SELECT udfMyFunction(user_id), someOtherField, etc FROM MyTable WHERE WhateverCondition
ここで、udfMyFunctionは、ユーザーIDを取り込み、それを使用して必要なことをすべて実行するために作成する関数です。
http://www.sqlteam.com/article/user-defined-functionsを参照してくださいを
カーソルは可能な限り避けなければならないことに同意します。そしてそれは通常可能です!
(もちろん、私の答えは、SPからの出力の取得のみに関心があり、実際のデータを変更しないことを前提としています。「特定の方法でユーザーデータを変更する」ことは、元の質問から少しあいまいです。だから私はこれを可能な解決策として提供すると思いました。実際にはあなたが何をしているのかに依存します!)
テーブル変数または一時テーブルを使用します。
前述したように、カーソルは最後の手段です。多くのリソースを使用し、ロックを発行し、SQLを適切に使用する方法を理解していないことを示している可能性があります。
補足:テーブルの行を更新するためにカーソルを使用するソリューションに出くわしました。精査した結果、すべてを1つのUPDATEコマンドで置き換えることができることがわかりました。ただし、この場合、ストアドプロシージャを実行する必要がある場合、単一のSQLコマンドは機能しません。
次のようなテーブル変数を作成します(大量のデータを処理している場合やメモリが不足している場合は、代わりに一時テーブルを使用してください)。
DECLARE @menus AS TABLE (
id INT IDENTITY(1,1),
parent NVARCHAR(128),
child NVARCHAR(128));
の id
重要です。
交換parent
してchild
いくつかの良いデータ、例えば、関連する識別子または上で動作するデータのセット全体で。
テーブルにデータを挿入します。例:
INSERT INTO @menus (parent, child)
VALUES ('Some name', 'Child name');
...
INSERT INTO @menus (parent,child)
VALUES ('Some other name', 'Some other child name');
いくつかの変数を宣言します。
DECLARE @id INT = 1;
DECLARE @parentName NVARCHAR(128);
DECLARE @childName NVARCHAR(128);
最後に、テーブル内のデータに対してwhileループを作成します。
WHILE @id IS NOT NULL
BEGIN
SELECT @parentName = parent,
@childName = child
FROM @menus WHERE id = @id;
EXEC myProcedure @parent=@parentName, @child=@childName;
SELECT @id = MIN(id) FROM @menus WHERE id > @id;
END
最初の選択は、一時テーブルからデータをフェッチします。2番目の選択は@idを更新します。MIN
行が選択されていない場合はnullを返します。
別の方法は、テーブルに行がある間にループしSELECT TOP 1
、選択した行を一時テーブルから削除することです。
WHILE EXISTS(SELECT 1 FROM @menuIDs)
BEGIN
SELECT TOP 1 @menuID = menuID FROM @menuIDs;
EXEC myProcedure @menuID=@menuID;
DELETE FROM @menuIDs WHERE menuID = @menuID;
END;
カーソルを使用せず、小さくて簡単なDave Rinconの動的クエリ方法が好きです。デイブ、共有してくれてありがとう。
しかし、Azure SQLでの必要性のために、クエリに "明確な"ものがあるため、次のようにコードを変更する必要がありました。
Declare @SQL nvarchar(max);
-- Set SQL Variable
-- Prepare exec command for each distinctive tenantid found in Machines
SELECT @SQL = (Select distinct 'exec dbo.sp_S2_Laser_to_cache ' +
convert(varchar(8),tenantid) + ';'
from Dim_Machine
where iscurrent = 1
FOR XML PATH(''))
--for debugging print the sql
print @SQL;
--execute the generated sql script
exec sp_executesql @SQL;
これが誰かを助けることを願っています...