SQL Server loop - bagaimana cara melakukan loop melalui serangkaian catatan

151

bagaimana cara memutus set rekaman dari pilih?

Jadi katakan misalnya saya punya beberapa catatan yang ingin saya lewati dan lakukan sesuatu dengan setiap catatan. Berikut ini adalah versi primitif dari pilih saya:

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

Terima kasih

Funky
sumber
5
Apa yang ingin Anda lakukan untuk setiap rekaman? Preferensi akan melakukan pekerjaan dalam query SQL. Kecuali bahwa Anda perlu menggunakan T-SQL, mungkin dengan kursor.
Gordon Linoff
2
Saya akan menggunakan kursor.
FloChanz
5
Itu akan sangat lambat - apakah tidak mungkin untuk menulis ulang proc yang tersimpan atau memindahkan beberapa logika keluar dari itu untuk bekerja dengan cara berbasis set?
Jembatan
2
@ Lucu apa yang dilakukan sproc? Seringkali kode dapat ditulis ulang dengan cara yang ditetapkan (yaitu menghindari loop) Jika Anda bersikeras Anda ingin melakukan operasi RBAR ( simple-talk.com/sql/t-sql-programming/… ) maka kursor adalah hal yang ingin Anda selidiki.
gvee
1
Mungkin Anda bisa menjelaskan apa yang akan Anda lakukan dengan data ini secara lebih rinci. Dalam kebanyakan kasus, Anda dapat dengan mudah menulis satu query SQL yang akan melakukan apa yang perlu Anda lakukan dalam satu tindakan alih-alih perulangan melalui catatan individual.
Alan Barber

Jawaban:

212

Dengan menggunakan T-SQL dan kursor seperti ini:

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;
FloChanz
sumber
5
Yang benar adalah menulis ulang proses sehingga tidak perlu diulang. Looping adalah pilihan yang sangat buruk dalam database.
HLGEM
23
Mungkin Anda benar tetapi dengan informasi yang diberikan dalam pertanyaan pada saat saya menulis jawaban, pengguna hanya ingin mengulang serangkaian data ... dan Kursor adalah cara untuk melakukannya.
FloChanz
16
Kursor hanyalah alat - umumnya tidak ada yang benar atau salah tentang mereka. Amati kinerja dan putuskan. Jawaban ini (kursor) adalah salah satu pilihan yang mungkin. Anda juga dapat menggunakan WHILE LOOP, CTE, dll.
Chains
2
@FrenkyB Ya Anda bisa. Lihat ini ... stackoverflow.com/questions/11035187/…
sam yi
2
Selamat, solusi Anda bahkan ada di msdn: msdn.microsoft.com/en-us/library/… dan saya sangat suka cara Anda menggunakan Tipe Data Bidang.
Pete
111

Inilah yang telah saya lakukan jika Anda perlu melakukan sesuatu yang berulang ... tetapi akan lebih bijaksana untuk mencari operasi yang ditetapkan terlebih dahulu.

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
sam yi
sumber
4
Menggunakan CURSOR (lihat jawaban di bawah) tampaknya menjadi solusi yang jauh lebih elegan.
Mikhail Glukhov
Mengapa jawaban ini memiliki lebih banyak suara daripada solusi kursor?
ataravati
29
@ataravati Karena solusi ini dibaca lebih bersih untuk banyak programmer daripada kursor. Sintaks untuk kursor agak canggung bagi sebagian orang.
Brian Webster
Terima kasih! Contoh saya dengan pembaruan dan grup dengan logika menggunakan kode di atas: pastebin.com/GAjUNNi9 . Mungkin akan bermanfaat bagi siapa saja.
Nigrimmist
dapat variabel digunakan sebagai nama kolom dalam pernyataan pembaruan di dalam loop? Sesuatu seperti "Perbarui TableName SET @ ColumnName = 2"
MH
28

Perubahan kecil pada jawaban sam yi (untuk keterbacaan yang lebih baik):

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
Aturan
sumber
1
@Bluish, jawaban ini mengoreksi jawaban sam yi. Koreksi ini terutama di dalam select @TableID = (...)pernyataan.
Simple Sandman
Saya pikir jawaban ini harus dipilih dari pertanyaan ini
sajadre
14

Dengan menggunakan kursor Anda dapat dengan mudah beralih melalui catatan satu per satu dan mencetak catatan secara terpisah atau sebagai satu pesan termasuk semua catatan.

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
Agnel Amodia
sumber
1
ini terlihat menarik. Saya ingin tahu apa arti pengidentifikasi @.
netskink
@ hanya untuk membedakan sebagai variabel.
Agnel Amodia
9

Hanya pendekatan lain jika Anda baik-baik saja menggunakan tabel temp. Saya telah menguji ini secara pribadi dan itu tidak akan menyebabkan pengecualian (bahkan jika tabel temp tidak memiliki data.)

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
Sandeep
sumber
Ini sangat aneh. Ini berisi banyak kesalahan, juga menggunakan dua variabel di mana satu berjalan dari 1 ke COUNT(*)dan yang kedua dari COUNT(*)ke 1 adalah aneh.
David Ferenczy Rogožan
Variabel MAXID digunakan untuk LOOP melalui. Variabel COUNTER digunakan untuk melakukan operasi pada catatan tertentu dalam tabel. Jika saya membaca pertanyaan itu berbicara tentang "memiliki beberapa catatan yang ingin saya lewati dan lakukan sesuatu dengan setiap catatan". Saya mungkin salah, tapi tolong tunjukkan apa yang salah di atas @DAWID
Sandeep
2
Saya pikir sudah jelas bagaimana Anda menggunakan variabel-variabel itu dalam kode Anda. Anda bisa saja memiliki WHILE (@COUTNER <= @ROWID)dan Anda tidak perlu mengurangi @ROWIDdalam setiap iterasi. BTW apa yang terjadi jika ROWIDs di tabel Anda tidak berkelanjutan (beberapa baris sebelumnya dihapus).
David Ferenczy Rogožan
1
Kapan Anda menyarankan menggunakan Tabel Temp daripada menggunakan Kursor? Apakah ini hanya pilihan desain, atau apakah seseorang memiliki kinerja yang lebih baik?
h0r53
4

Anda dapat memilih untuk memberi peringkat data Anda dan menambahkan ROW_NUMBER dan menghitung mundur ke nol saat iterate dataset Anda.

-- 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
Bunkerbuster
sumber
2

dengan cara ini kita bisa beralih ke data tabel.

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 adalah fungsi yang ditentukan pengguna yang akan mem-parsing data yang dipisahkan koma dan tabel kembali. Terima kasih

Monojit Sarkar
sumber
1

Saya pikir ini adalah contoh cara mudah untuk beralih item.

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
江明哲
sumber