既存のコードでSQLServerカーソルを置き換えた経験、または手続き型の人がカーソルを使用して解決する問題をどのように取り、セットベースで実行したかを知りたいです。
カーソルを使用して解決した問題は何でしたか?カーソルをどのように置き換えましたか?
既存のコードでSQLServerカーソルを置き換えた経験、または手続き型の人がカーソルを使用して解決する問題をどのように取り、セットベースで実行したかを知りたいです。
カーソルを使用して解決した問題は何でしたか?カーソルをどのように置き換えましたか?
ループしないようにして、データセットで作業してください。
一度に複数の行を挿入、更新、削除できます。ここでは、複数の行の挿入例を示します。
INSERT INTO YourTable
(col1, col2, col3, col4)
SELECT
cola, colb+Colz, colc, @X
FROM ....
LEFT OUTER JOIN ...
WHERE...
ループを見るときは、ループ内で何が行われたかを確認してください。挿入/削除/更新だけの場合は、単一のコマンドを使用するように書き直してください。IFがある場合は、それらが挿入/削除/更新のCASEステートメントまたはWHERE条件である可能性があるかどうかを確認してください。その場合は、ループを削除してsetコマンドを使用します。
ループを取得してsetベースのコマンドに置き換え、実行時間を数分から数秒に短縮しました。多くのネストされたループとプロシージャ呼び出しを含むプロシージャを実行し、ループを保持しました(挿入/削除/更新のみを使用することは不可能でした)が、カーソルを削除し、ロック/ブロックが少なくなり、パフォーマンスが大幅に向上しました。カーソルループよりも優れた2つのループ方法を次に示します。
ループする必要がある場合は、セットに対して次のようにします。
--this looks up each row for every iteration
DECLARE @msg VARCHAR(250)
DECLARE @hostname sysname
--first select of currsor free loop
SELECT @hostname= min(RTRIM(hostname))
FROM master.dbo.sysprocesses (NOLOCK)
WHERE hostname <> ''
WHILE @hostname is not null
BEGIN
set @msg='exec master.dbo.xp_cmdshell "net send '
+ RTRIM(@hostname) + ' '
+ 'testing "'
print @msg
--EXEC (@msg)
--next select of cursor free loop
SELECT @hostname= min(RTRIM(hostname))
FROM master.dbo.sysprocesses (NOLOCK)
WHERE hostname <> ''
and hostname > @hostname
END
ループする適切なアイテムのセット(100,000ではない)がある場合は、次のように実行できます。
--this will capture each Key to loop over
DECLARE @msg VARCHAR(250)
DECLARE @From int
DECLARE @To int
CREATE TABLE #Rows
(
RowID int not null primary key identity(1,1)
,hostname varchar(100)
)
INSERT INTO #Rows
SELECT DISTINCT hostname
FROM master.dbo.sysprocesses (NOLOCK)
WHERE hostname <> ''
SELECT @From=0,@To=@@ROWCOUNT
WHILE @From<@To
BEGIN
SET @From=@From+1
SELECT @msg='exec master.dbo.xp_cmdshell "net send '
+ RTRIM(hostname) + ' '
+ 'testing "'
FROM #Rows WHERE RowID=@From
print @msg
--EXEC (@msg)
END
一部のカーソルをWHILEループに置き換えました。
DECLARE @SomeTable TABLE
(
ID int IDENTITY (1, 1) PRIMARY KEY NOT NULL,
SomeNumber int,
SomeText varchar
)
DECLARE @theCount int
DECLARE @theMax int
DECLARE @theNumber int
DECLARE @theText varchar
INSERT INTO @SomeTable (SomeNumber, SomeText)
SELECT Number, Text
FROM PrimaryTable
SET @theCount = 1
SELECT @theMax = COUNT(ID) FROM @SomeTable
WHILE (@theCount <= @theMax)
BEGIN
SET @theNumber = 0
SET @theText = ''
SELECT @theNumber = IsNull(Number, 0), @theText = IsNull(Text, 'nothing')
FROM @SomeTable
WHERE ID = @theCount
-- Do something.
PRINT 'This is ' + @theText + ' from record ' + CAST(@theNumber AS varchar) + '.'
SET @theCount = @theCount + 1
END
PRINT 'Done'
手続き型プログラミングに慣れているアプリ開発者は、SQLであっても、習慣からすべてを手続き的に実行しようとすることがよくあります。
ほとんどの場合、適切なパラメータを使用したSELECTで十分な場合があります。または、UPDATEステートメントを処理している場合もあります。
重要なのは、セット操作について考え始め、RDBMSに何をしたいのかを伝える必要があるということです。段階的に行う方法ではありません。
これに単一の「正しい」答えを与えるのは難しいです.....あなたはほとんど具体的な例でそれを示さなければならないでしょう。
マーク
特定の年に関連する財務データの現在の合計を計算するコードをいくつか作成しました。各四半期で、NULLを適切に処理しながら、現在の四半期の値を現在の合計に追加して、現在の四半期の値がNULLのときに前の四半期の現在の合計が繰り越されるようにする必要がありました。
もともと、私はカーソルを使用してこれを行いましたが、機能的な観点から、これはビジネス要件を満たしていました。技術的な観点からは、データの量が増えるにつれてコードに指数関数的に時間がかかるため、それは目立たないものであることが判明しました。解決策は、カーソルを、機能要件を満たし、パフォーマンスの問題を排除する相関サブクエリに置き換えることでした。
お役に立てれば、
明細書