特定の方法でユーザーデータを変更するストアドプロシージャがあります。私はそれを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テーブルが表示され、それを処理できます。できれば、データセット全体で、カーソルやループを使用せずに作業できます。
これは、この子ストアドプロシージャの実行内容によって異なります。更新している場合は、#tempテーブルに「更新元」として参加し、ループなしで1つのステートメントですべての作業を実行できます。 INSERTとDELETEについても同じことができます。 IFで複数の更新を行う必要がある場合は、#tempテーブルを使用してそれらを複数のUPDATE FROM
に変換し、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
動的なクエリでそれを行うことができます。
declare @cadena varchar(max) = ''
select @cadena = @cadena + 'exec spAPI ' + ltrim(id) + ';'
from sysobjects;
exec(@cadena);
これは、ストアドプロシージャが実行していることをすべて複製するユーザー定義関数では実行できませんか?
SELECT udfMyFunction(user_id), someOtherField, etc FROM MyTable WHERE WhateverCondition
ここで、udfMyFunctionは、ユーザーIDを取得し、ユーザーIDを使用して必要なことを行うユーザー作成関数です。
もう少し背景については http://www.sqlteam.com/article/user-defined-functions をご覧ください
カーソルは可能な限り避けるべきであることに同意します。そして、それは通常可能です!
(もちろん、私の答えは、あなたがSPからの出力の取得にのみ関心があり、実際のデータを変更しないことを前提としています。「特定の方法でユーザーデータを変更する」元の質問から曖昧なので、可能な解決策としてこれを提供すると思いました。完全にあなたが何をしているのかに依存します!)
テーブル変数または一時テーブルを使用します。
前述したように、カーソルは最後の手段です。主に多くのリソースを使用し、ロックを発行し、SQLを適切に使用する方法を理解していない兆候である可能性があります。
補足:カーソルを使用してテーブルの行を更新するソリューションに出くわしました。いくつかの精査の後、全体が単一のUPDATEコマンドで置き換えられることが判明しました。ただし、この場合、ストアドプロシージャを実行する必要がある場合、単一のSQLコマンドは機能しません。
次のようなテーブル変数を作成します(大量のデータを処理している場合、またはメモリが不足している場合は、代わりに temporary table を使用します)。
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;
これが誰かを助けることを願っています...