SQL Serverループ-レコードのセットをループする方法


151

選択からレコードのセットをループするにはどうすればよいですか?

たとえば、いくつかのレコードをループして、各レコードを処理したいとします。これが私の選択の原始バージョンです:

select top 1000 * from dbo.table
where StatusID = 7 

ありがとう


5
各レコードに何をしたいですか?作業はSQLクエリで行うことをお勧めします。おそらくカーソルでT-SQLを使用する必要がある場合を除きます。
ゴードンリノフ2013

2
カーソルを使います。
FloChanz 2013

5
それは非常に遅くなります-ストアドプロシージャを書き換えたり、ロジックの一部をそこから移動して、セットベースの方法で機能させることはできませんか?
ブリッジ

2
@ファンキーsprocは何をしますか?多くの場合、コードはセットベースの方法で(つまり、ループを回避して)書き換えることができます。RBAR操作(simple-talk.com/sql/t-sql-programming/…)を実行したい場合は、カーソルが調査対象となります。
2013

1
おそらく、このデータを使って何をするのかをより詳しく説明できます。ほとんどの場合、個々のレコードをループする代わりに、1つのアクションで実行する必要があることを実行する単一のSQLクエリを簡単に作成できます。
アランバーバー

回答:


212

T-SQLとカーソルを次のように使用する:

DECLARE @MyCursor CURSOR;
DECLARE @MyField YourFieldDataType;
BEGIN
    SET @MyCursor = CURSOR FOR
    select top 1000 YourField from dbo.table
        where StatusID = 7      

    OPEN @MyCursor 
    FETCH NEXT FROM @MyCursor 
    INTO @MyField

    WHILE @@FETCH_STATUS = 0
    BEGIN
      /*
         YOUR ALGORITHM GOES HERE   
      */
      FETCH NEXT FROM @MyCursor 
      INTO @MyField 
    END; 

    CLOSE @MyCursor ;
    DEALLOCATE @MyCursor;
END;

5
正しいことは、ループする必要がないようにプロセスを書き直すことです。ループはデータベースでは非常に悪い選択です。
HLGEM 2013

23
多分あなたは正しいですが、私が答えを書いたときに質問で与えられた情報を使用して、ユーザーはデータのセットをループしたいだけです...そしてカーソルはそれを行う方法です。
FloChanz 2013

16
カーソルは単なるツールであり、一般的に正しいことや間違っていることはありません。パフォーマンスを観察して決定します。この答え(カーソル)は1つの選択肢です。WHILE LOOP、CTEなどを使用することもできます
チェーン

2
@FrenkyBはい、できます。このように見てください... stackoverflow.com/questions/11035187/…–
sam yi

2
おめでとうございます。あなたのソリューションはmsdn:msdn.microsoft.com/en-us/library/…にもあります。フィールドデータ型の使い方がとても気に入っています。
Pete

111

これは、反復的な処理が必要な場合に私が行ってきた方法ですが、最初に集合演算を探すのが賢明です。

select top 1000 TableID
into #ControlTable 
from dbo.table
where StatusID = 7

declare @TableID int

while exists (select * from #ControlTable)
begin

    select top 1 @TableID = TableID
    from #ControlTable
    order by TableID asc

    -- Do something with your TableID

    delete #ControlTable
    where TableID = @TableID

end

drop table #ControlTable

4
CURSOR(以下の回答を参照)を使用する方がはるかに洗練されたソリューションのようです。
ミハイルグルホフ2015

この回答には、カーソルソリューションよりも多くの賛成票があるのはなぜですか?
ataravati 2015年

29
@ataravatiこのソリューションは、カーソルよりも多くのプログラマーにきれいに読み取れるためです。一部の人にとって、カーソルの構文はかなり扱いにくいものです。
ブライアンウェブスター

ありがとうございました!コード上に使用してロジックにより更新グループと私の例: pastebin.com/GAjUNNi9。多分誰にとっても役に立つでしょう。
Nigrimmist 2016年

変数をループ内の更新ステートメントの列名として使用できますか?「Update TableName SET @ ColumnName = 2」のようなもの
MH

28

sam yiの答えに小さな変更を加えました(読みやすくするため):

select top 1000 TableID
into #ControlTable 
from dbo.table
where StatusID = 7

declare @TableID int

while exists (select * from #ControlTable)
begin

    select @TableID = (select top 1 TableID
                       from #ControlTable
                       order by TableID asc)

    -- Do something with your TableID

    delete #ControlTable
    where TableID = @TableID

end

drop table #ControlTable

1
@青みがかった、この答えはサムイーの答えを修正しています。この修正は主にselect @TableID = (...)ステートメント内にあります。
シンプルなサンドマン

この回答はこの質問から選択する必要があると思います
sajadre

14

カーソルを使用すると、レコードを簡単に繰り返し処理して、レコードを個別に、またはすべてのレコードを含む単一のメッセージとして印刷できます。

DECLARE @CustomerID as INT;
declare @msg varchar(max)
DECLARE @BusinessCursor as CURSOR;

SET @BusinessCursor = CURSOR FOR
SELECT CustomerID FROM Customer WHERE CustomerID IN ('3908745','3911122','3911128','3911421')

OPEN @BusinessCursor;
    FETCH NEXT FROM @BusinessCursor INTO @CustomerID;
    WHILE @@FETCH_STATUS = 0
        BEGIN
            SET @msg = '{
              "CustomerID": "'+CONVERT(varchar(10), @CustomerID)+'",
              "Customer": {
                "LastName": "LastName-'+CONVERT(varchar(10), @CustomerID) +'",
                "FirstName": "FirstName-'+CONVERT(varchar(10), @CustomerID)+'",    
              }
            }|'
        print @msg
    FETCH NEXT FROM @BusinessCursor INTO @CustomerID;
END

1
これは面白いですね。@識別子が何を意味するのかしら。
netskink

@は変数として区別するためのものです。
Agnel Amodia

9

一時テーブルをうまく使用している場合の別のアプローチ。私はこれを個人的にテストしましたが、(一時テーブルにデータがない場合でも)例外は発生しません。

CREATE TABLE #TempTable
(
    ROWID int identity(1,1) primary key,
    HIERARCHY_ID_TO_UPDATE int,
)

--create some testing data
--INSERT INTO #TempTable VALUES(1)
--INSERT INTO #TempTable VALUES(2)
--INSERT INTO #TempTable VALUES(4)
--INSERT INTO #TempTable VALUES(6)
--INSERT INTO #TempTable VALUES(8)

DECLARE @MAXID INT, @Counter INT

SET @COUNTER = 1
SELECT @MAXID = COUNT(*) FROM #TempTable

WHILE (@COUNTER <= @MAXID)
BEGIN
    --DO THE PROCESSING HERE 
    SELECT @HIERARCHY_ID_TO_UPDATE = PT.HIERARCHY_ID_TO_UPDATE
    FROM #TempTable AS PT
    WHERE ROWID = @COUNTER

    SET @COUNTER = @COUNTER + 1
END


IF (OBJECT_ID('tempdb..#TempTable') IS NOT NULL)
BEGIN
    DROP TABLE #TempTable
END

これは本当に奇妙です。これには多くのエラーが含まれます。1つが1にCOUNT(*)、2つ目が1に変わる2つの変数を使用COUNT(*)することは奇妙です。
David FerenczyRogožan2016

変数MAXIDは、LOOPに使用されます。変数COUNTERは、テーブル内の特定のレコードに対して操作を実行するために使用されます。質問を読むと、「ループして、各レコードで何かしたいレコードがいくつかあります」と書かれています。私は間違っているかもしれませんが、@ DAWIDの何が間違っているか指摘してください
Sandeep

2
これらの変数をコードで使用する方法は明らかだと思います。あなたはただ持つことができ、各反復でWHILE (@COUTNER <= @ROWID)デクリメントする必要はありません@ROWID。ところでROWID、テーブル内のsが連続的でない場合はどうなりますか(一部の行は以前に削除されています)。
David FerenczyRogožan2016

1
カーソルを使用するよりも一時テーブルを使用することをお勧めしますか?これは単なる設計上の選択ですか、それともパフォーマンスが優れているのですか?
h0r53

4

データをランク付けしてROW_NUMBERを追加し、データセットを反復しながらゼロまでカウントダウンすることを選択できます。

-- Get your dataset and rank your dataset by adding a new row_number
SELECT  TOP 1000 A.*, ROW_NUMBER() OVER(ORDER BY A.ID DESC) AS ROW
INTO #TEMPTABLE 
FROM DBO.TABLE AS A
WHERE STATUSID = 7;

--Find the highest number to start with
DECLARE @COUNTER INT = (SELECT MAX(ROW) FROM #TEMPTABLE);
DECLARE @ROW INT;

-- Loop true your data until you hit 0
WHILE (@COUNTER != 0)
BEGIN

    SELECT @ROW = ROW
    FROM #TEMPTABLE
    WHERE ROW = @COUNTER
    ORDER BY ROW DESC

    --DO SOMTHING COOL  

    -- SET your counter to -1
    SET @COUNTER = @ROW -1
END

DROP TABLE #TEMPTABLE

2

このようにして、テーブルデータに反復できます。

DECLARE @_MinJobID INT
DECLARE @_MaxJobID INT
CREATE  TABLE #Temp (JobID INT)

INSERT INTO #Temp SELECT * FROM DBO.STRINGTOTABLE(@JobID,',')
SELECT @_MinJID = MIN(JobID),@_MaxJID = MAX(JobID)  FROM #Temp

    WHILE @_MinJID <= @_MaxJID
    BEGIN

        INSERT INTO Mytable        
        (        
            JobID,        
        )        

        VALUES        
        (        
            @_MinJobID,        
        ) 

        SET @_MinJID = @_MinJID + 1;
    END

DROP TABLE #Temp

STRINGTOTABLEは、コンマ区切りのデータを解析してテーブルを返すユーザー定義関数です。ありがとう


1

これはアイテムを反復する簡単な方法の例だと思います。

declare @cateid int
select CateID into [#TempTable] from Category where GroupID = 'STOCKLIST'

while (select count(*) from #TempTable) > 0
begin
    select top 1 @cateid = CateID from #TempTable
    print(@cateid)

    --DO SOMETHING HERE

    delete #TempTable where CateID = @cateid
end

drop table #TempTable
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.