カーソルの一般的な置換方法を教えてください。私が見ているカーソルの一般的な実装は
DECLARE @variable INT, @sqlstr NVARCHAR(MAX)
DECLARE cursor_name CURSOR
FOR select_statement --essentially to get an array for @variable
--usually it's a subset of unique ids for accounts, clients, parts, etc
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO @variable
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sqlstr = N'
/* some query that uses '+ str(@variable) +' to do dirty work
such as: go through all our accounts, if it''s some subset (possible new cursor),
go through those accounts and connect this way,
map those fields and add it to our big uniform table */
'
EXEC sp_executesql @sqlstr
FETCH NEXT FROM cursor_name INTO @variable
END
CLOSE cursor_name
DEALLOCATE cursor_name
非常に多くの人々がアンチカーソルであるので( に同意する:SO:なぜ人々はカーソルを嫌うのか )一般的な実装の一般的な置き換えは何ですか(できればSQLサーバー)?
1つまたは複数のカーソルを回避する機能は、このカーソル内で実行される内容によって異なります。その中で何が起こっているのかを知らなければ、伝える方法はありません。回避策がない可能性があり、行ごとに処理する必要があります。
以下にいくつかの例を示します。
この例は最も基本的な例であり、データセット全体またはデータセットの一部を一度にクエリできますが、カーソルが作成され、行ごとにデータをクエリしています。これを置き換える一般的なものは、JOIN
、_CROSS APPLY
_/_OUTER APPLY
_などです。
次のデータセットを検討してください:
_CREATE TABLE dbo.Lotr(LotrId int, CharacterName varchar(255), Val varchar(255));
CREATE TABLE dbo.LotrAttributes(LotrATtributeId int, LotrId int, AttrVal varchar(255));
INSERT INTO dbo.Lotr(LotrId,CharacterName,Val)
VALUES(1,'Frodo','Ring')
,(2,'Gandalf','Staff');
INSERT INTO dbo.LotrAttributes(LotrId,LotrATtributeId,AttrVal)
VALUES(1,1,'RingAttribute1')
,(1,2,'RingAttribute2')
,(1,3,'RingAttribute3')
,(2,4,'StaffAttribute1')
,(2,5,'StaffAttribute2');
_
Lotr
テーブルをループすることで、各レコードを検索して個別に一致させることができます。
カーソル:
_DECLARE @LotrID int
DECLARE C CURSOR FOR SELECT LotrId from dbo.Lotr;
OPEN C
FETCH NEXT FROM C INTO @LotrID;
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT LotrATtributeId from dbo.LotrAttributes where LotrId = @LotrID;
FETCH NEXT FROM C INTO @LotrID;
END
CLOSE C
DEALLOCATE C
_
結果は2つの結果セットになります
_LotrATtributeId
1
2
3
LotrATtributeId
4
5
_
この_inner join
_を使用すると、1つの結果セットと同じ結果が得られます。
_SELECT LotrATtributeId from dbo.Lotr L
INNER JOIN dbo.LotrAttributes LA
ON L.LotrId = LA.LotrId;
LotrATtributeId
1
2
3
4
5
_
一般的なのは、カーソル内の文字列操作を置き換えるためにFOR XML PATH('')
を使用することです。
データセット
_CREATE TABLE dbo.Lotr(LotrId int, CharacterName varchar(255), Val varchar(255));
CREATE TABLE dbo.LotrAttributes(LotrATtributeId int, LotrId int, AttrVal varchar(255));
INSERT INTO dbo.Lotr(LotrId,CharacterName,Val)
VALUES(1,'Frodo','Ring');
INSERT INTO dbo.LotrAttributes(LotrId,LotrATtributeId,AttrVal)
VALUES(1,1,'RingAttribute1')
,(1,2,'RingAttribute2')
,(1,3,'RingAttribute3');
_
文字列操作付きのダブルカーソル
_DECLARE @LotrId int, @CharacterName varchar(255), @Val varchar(255)
DECLARE @LotrATtributeId int, @AttrVal varchar(255)
DECLARE C CURSOR FOR
SELECT LotrId,CharacterName, Val FROM dbo.Lotr
OPEN C
FETCH NEXT FROM C INTO @LotrId,@CharacterName,@Val
WHILE @@FETCH_STATUS = 0
BEGIN
SET @CharacterName +='|'+ @Val
DECLARE D CURSOR FOR
SELECT LotrATtributeId, AttrVal FROM dbo.LotrAttributes where LotrId = @LotrId
OPEN D
FETCH NEXT FROM D INTO @LotrATtributeId,@AttrVal
WHILE @@FETCH_STATUS = 0
BEGIN
SET @CharacterName +='['+@AttrVal+ '],'
FETCH NEXT FROM D INTO @LotrATtributeId,@AttrVal
END
CLOSE D
DEALLOCATE D
FETCH NEXT FROM C INTO @LotrId,@CharacterName,@Val
END
CLOSE C
DEALLOCATE C
SELECT LEFT(@CharacterName,len(@charactername)-1);
_
結果
_(No column name)
Frodo|Ring[RingAttribute1],[RingAttribute2],[RingAttribute3],
_
FOR XML PATH( '')を使用してカーソルを削除する
_SELECT L.Charactername +'|'+ L.Val + (SELECT stuff((SELECT ','+QUOTENAME(AttrVal) FROM dbo.LotrAttributes LA WHERE LA.LotrId = L.LotrId FOR XML PATH('')), 1, 1, ''))
FROM
dbo.Lotr L;
_
ここでの実際の回避策は、データがこのように表示される理由を理解し、アプリケーションを変更することです。..この形式でデータを必要としないように、どこかに保存します。
あなたの手が結ばれている場合、これは次善の策です。
データ
CREATE TABLE dbo.sometable(InsertTableId int、val varchar(255)); CREATE TABLE dbo.Top10Table(Top10TableId int、InsertTableId int、val varchar(255));
_INSERT INTO dbo.sometable(InsertTableId,val)
VALUES(1,'bla')
,(2,'blabla');
INSERT INTO dbo.Top10Table(Top10TableId,InsertTableId,Val)
VALUES(1,1,'WUW')
,(2,1,'WUW')
,(3,1,'WUW');
_
カーソル
_CREATE TABLE #Top10Values(Top10TableId int, InsertTableId int, val varchar(255))
DECLARE @InsertTableId int;
DECLARE C CURSOR FOR select InsertTableId from dbo.sometable;
OPEN C
FETCH NEXT FROM C INTO @InsertTableId;
WHILE @@FETCH_STATUS =0
BEGIN
INSERT INTO #Top10Values(Top10TableId,InsertTableId,val)
SELECT top(10) Top10TableId,InsertTableId,Val FROM dbo.Top10Table
where InsertTableId = @InsertTableId
ORDER BY Top10TableId
FETCH NEXT FROM C INTO @InsertTableId;
END
CLOSE C
DEALLOCATE C
SELECT * FROM #Top10Values;
DROP TABLE #Top10Values;
_
結果
_Top10TableId InsertTableId val
1 1 WUW
2 1 WUW
3 1 WUW
_
カーソルを_CROSS APPLY
_とCTE
で置き換える
_CREATE TABLE #Top10Values(Top10TableId int, InsertTableId int, val varchar(255));
;WITH CTE
AS
(
select InsertTableId from dbo.sometable
)
INSERT INTO #Top10Values(Top10TableId,InsertTableId,val)
SELECT T1T.Top10TableId,T1T.InsertTableId,T1T.Val
FROM
CTE
CROSS APPLY (SELECT TOP (10) Top10TableId,InsertTableId,Val from dbo.Top10Table T1T
WHERE T1T.InsertTableId = CTE.InsertTableId
) T1T ;
SELECT * FROM #Top10Values;
DROP TABLE #Top10Values;
_
時々他に選択肢がない
セットで作業できず、行ごとの処理を行う必要がある場合でも、カーソルを最適化できます。
カーソルの高速化における最大の変更点の1つは、カーソルに_LOCAL FAST_FORWARD
_を追加することです。
_DECLARE C CURSOR LOCAL FAST_FORWARD FOR SELECT LotrId from dbo.Lotr
_
LOCAL
&_FAST_FORWARD
_などのカーソル設定を使用した場合と使用しない場合のパフォーマンスの違いの可能性について説明している@AaronBertrandによる このブログ投稿 をご覧ください。
「一般的な置き換え」はありません。ここでは「汚い仕事」をすべて隠しているため、この場合にspecificの置き換えがあるかどうかを判断するのは困難です。カーソルを使用するか、whileループを使用するか、またはを処理するセットベースのプロセスに変換するその他の反復プロセスを使用するかに関わらず、一度に1行ずつ一連の行を処理する特定のケースがあります。すべて行を一度にはるかに優れています。ただし、ストアドプロシージャの実行や行ごとの動的SQLの実行、複数のデータベースにわたる同じクエリなど、一度に1行ずつ実行する必要のあるものもあります。
カーソルであるかどうかに関係なく、宣言とリンクの問題は、declare cursorを使用しても他のループ構造体を使用しても同じです( this post を参照)。また、実行する必要がある場合は関係ありません。とにかく一度に1行ずつ行われます。したがって、このカーソルの動作に関する特定の詳細を提供すると、カーソルを削除する方法(またはできないこと)に関するアドバイスが得られる可能性がありますが、適用できる魔法のようなすべてのカーソルを削除するアプローチを検索すると、すべてのシナリオにあなたのためにかなりイライラするでしょう。
言語IMHOを入力する新しい人々に対する一般的なアドバイスは、行のセットに対して、常に何をする必要があるかを考えることですセットの各行に。言語の違いは微妙ですが、重要です。問題を個々の行の束ではなくデータのセットとして考える場合、デフォルトでカーソルを使用する可能性は低くなります。しかし、SQL Serverがそのように機能するように最適化されていないことを単に教える以外に、反復型が最良/唯一の方法であるさまざまな種類のプログラミングからのものである場合、それを明白にする方法や、自動。
あなたの質問はまだ一般的な交換を求めています、そして私はまだそのようなことはないと信じています。
ダグレーンは、YouTubeにある「T-SQLレベルアップ」と呼ばれる一連のビデオを制作しました。シリーズの一部では、次のようなカーソルを削除する一般的な方法について説明します。
SELECT
で後で使用されるINSERT
によって入力された変数は、INSERT INTO...SELECT
ステートメント、たとえば)IF...ELSE
)_WHERE
句、CASE
ステートメント、サブクエリなどにここで他の素晴らしい答えが指摘しているように、これに対する特効薬はありません。しかし、私の意見では、これらのビデオは問題を解決するための非常に直感的なアプローチです。
Dougは、各部分で複雑さが増している3つのカーソルの置換を行っています。ビデオで全体の取引がよりよく見られるため、私は視聴することを強くお勧めします。