SQL Server döngüsü - nasıl bir kayıt kümesinde döngü


151

bir seçimlerin kayıt kümesinde nasıl döngü yapabilirim?

Diyelim ki her bir kayıtta bir şey yapmak istediğim birkaç kayıt var. İşte benim seçimimin ilkel bir versiyonu:

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

Teşekkürler


5
Her kayıt için ne yapmak istiyorsunuz? Tercih, bir SQL sorgusunda çalışmaktır. Belki de imleçlerle birlikte T-SQL kullanmanız gerekeceği için.
Gordon Linoff

2
Bir İmleç kullanırdım.
FloChanz

5
Bu oldukça yavaş olacak - saklanan proc'u yeniden yazmak veya mantığın bir kısmını ayarlanmış bir şekilde çalışmak için dışarı taşımak mümkün değil mi?
Köprü

2
@Funky Sproc ne yapıyor? Çoğu zaman kod ayarlanmış bir şekilde yeniden yazılabilir (yani döngülerden kaçının). Eğer bir RBAR işlemi ( simple-talk.com/sql/t-sql-programming/… ) yapmak istediğinizi düşünüyorsanız , imleç araştırmak istediğiniz şeydir.
gvee

1
Belki bu verilerle ne yapacağınızı daha ayrıntılı olarak açıklayabilirsiniz. Çoğu durumda, tek tek kayıtlarda döngü yapmak yerine tek bir eylemde yapmanız gerekenleri yapacak tek bir SQL sorgusu kolayca yazabilirsiniz.
Alan Barber

Yanıtlar:


212

T-SQL ve bunun gibi imleçler kullanarak:

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
Doğru olan şey, döngüye gerek kalmaması için süreci yeniden yazmaktır. Döngü bir veritabanında son derece kötü bir seçimdir.
HLGEM

23
Belki haklısınız ama cevap yazarken soruda verilen bilgilerle kullanıcı sadece bir veri kümesi arasında döngü yapmak istiyor ... ve bir İmleç bunu yapmanın bir yoludur.
FloChanz

16
İmleçler sadece bir araçtır - onlar hakkında genellikle doğru ya da yanlış hiçbir şey yoktur. Performansı gözlemleyin ve karar verin. Bu cevap (imleçler) olası bir seçenektir. Ayrıca WHILE vb LOOP, CTE kullanabilirsiniz
Zincirleri

2
@FrenkyB Evet yapabilirsiniz. Bu tarafa bak ... stackoverflow.com/questions/11035187/…
sam yi

2
Tebrikler, çözümünüz msdn: msdn.microsoft.com/en-us/library/… adresinde bile ve Alan Veri Türünü nasıl kullandığınızı gerçekten seviyorum.
Pete

111

Yinelemeli bir şey yapmanız gerekiyorsa yaptığım şey budur ... ama önce ayarlanmış işlemleri aramak akıllıca olur.

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
Bir CURSOR kullanmak (aşağıdaki cevaba bakınız) çok daha şık bir çözüm gibi görünüyor.
Mikhail Glukhov

Bu cevap neden imleç çözümünden daha fazla oy alıyor?
ataravati

29
@ataravati Çünkü bu çözüm birçok programcıya imleçlerden daha temiz okur. İmleçlerin sözdizimi bazıları için oldukça gariptir.
Brian Webster

Teşekkür ederim! Yukarıdaki kodu kullanarak mantığa göre güncelleme ve gruplama ile Örneğim : pastebin.com/GAjUNNi9 . Belki herkes için yararlı olacaktır.
Nigrimmist

değişken döngü içindeki güncelleme deyiminde sütun adı olarak kullanılabilir mi? "Update TableName SET @ ColumnName = 2" gibi bir şey
MH

28

Sam Yi'nin cevabında küçük değişiklik (daha iyi okunabilirlik için):

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
@bluish, bu cevap sam yi'nin cevabını düzeltiyor. Bu düzeltme esas olarak select @TableID = (...)ifadenin içindedir .
Basit Sandman

Bence bu sorunun bu sorudan seçilmesi gerekiyor
sajadre

14

İmleç kullanarak kayıtları tek tek kolayca tekrarlayabilir ve kayıtları ayrı ayrı veya tüm kayıtları içeren tek bir mesaj olarak yazdırabilirsiniz.

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
bu ilginç görünüyor. @ Tanımlayıcının ne anlama geldiğini merak ediyorum.
netskink

@ sadece değişken olarak ayırt etmektir.
Agnel Amodia

9

Geçici tabloları kullanarak iyi iseniz başka bir yaklaşım.Kişisel olarak bunu test ettim ve herhangi bir istisna neden olmaz (temp tablosunda veri olmasa bile).

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

Bu gerçekten garip. Bu hataların bir sürü içerir aynı zamanda bir 1'den gider iki değişkenin kullanarak COUNT(*)ve ikinci gider COUNT(*)1'e garip.
David Ferenczy Rogožan

MAXID değişkeni LOOP aracılığıyla kullanılır. COUNTER değişkeni, tablodaki belirli bir kayıtta bir işlem gerçekleştirmek için kullanılır. Soruyu okursam, "her bir kayıtta bir döngü yapmak ve bir şey yapmak istediğim birkaç kayıt var" hakkında konuşur. Yanlış olabilirim ama lütfen yukarıda neyin yanlış olduğunu belirtin @DAWID
Sandeep

2
Kodunuzda bu değişkenleri nasıl kullandığınız çok açık. Her yinelemede sadece sahip olabilirsiniz WHILE (@COUTNER <= @ROWID)ve azalmanız gerekmez @ROWID. BTW ROWIDtablonuzdaki s sürekli değilse (bazı satırlar daha önce silinmişse) ne olur ?
David Ferenczy Rogožan

1
Ne zaman bir İmleç kullanarak bir Temp Tablosu kullanmanızı önerirsiniz? Bu sadece bir tasarım mı yoksa daha iyi bir performans mı var?
h0r53

4

Verilerinizi sıralayıp bir ROW_NUMBER ekleyebilir ve veri kümenizi yinelerken sıfıra kadar geri sayabilirsiniz.

-- 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

bu şekilde tablo verilerini yineleyebiliriz.

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 , virgülle ayrılmış verileri ve dönüş tablosunu ayrıştıracak kullanıcı tanımlama işlevidir. Teşekkürler


1

Bu öğeyi yinelemenin kolay yolu olduğunu düşünüyorum.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.