5

既存のコードでSQLServerカーソルを置き換えた経験、または手続き型の人がカーソルを使用して解決する問題をどのように取り、セットベースで実行したかを知りたいです。

カーソルを使用して解決した問題は何でしたか?カーソルをどのように置き換えましたか?

4

4 に答える 4

9

ループしないようにして、データセットで作業してください。

一度に複数の行を挿入、更新、削除できます。ここでは、複数の行の挿入例を示します。

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
于 2009-06-08T21:29:26.787 に答える
4

一部のカーソルを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'
于 2009-06-09T17:46:14.333 に答える
2

手続き型プログラミングに慣れているアプリ開発者は、SQLであっても、習慣からすべてを手続き的に実行しようとすることがよくあります。

ほとんどの場合、適切なパラメータを使用したSELECTで十分な場合があります。または、UPDATEステートメントを処理している場合もあります。

重要なのは、セット操作について考え始め、RDBMSに何をしたいのかを伝える必要があるということです。段階的に行う方法ではありません。

これに単一の「正しい」答えを与えるのは難しいです.....あなたはほとんど具体的な例でそれを示さなければならないでしょう。

マーク

于 2009-06-08T21:14:37.550 に答える
0

特定の年に関連する財務データの現在の合計を計算するコードをいくつか作成しました。各四半期で、NULLを適切に処理しながら、現在の四半期の値を現在の合計に追加して、現在の四半期の値がNULLのときに前の四半期の現在の合計が繰り越されるようにする必要がありました。

もともと、私はカーソルを使用してこれを行いましたが、機能的な観点から、これはビジネス要件を満たしていました。技術的な観点からは、データの量が増えるにつれてコードに指数関数的に時間がかかるため、それは目立たないものであることが判明しました。解決策は、カーソルを、機能要件を満たし、パフォーマンスの問題を排除する相関サブクエリに置き換えることでした。

お役に立てれば、

明細書

于 2009-06-08T22:00:31.450 に答える