Bucla SQL Server – cum se face bucla printr-un set de înregistrări (Programare, Sql, Server Sql)

Funky a intrebat.

cum pot să parcurg în buclă un set de înregistrări dintr-un select?

Să zicem, de exemplu, că am câteva înregistrări prin care doresc să trec în buclă și să fac ceva cu fiecare înregistrare. Iată o versiune primitivă a selectării mele:

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

Mulțumesc

Comentarii

  • Ce vrei să faci cu fiecare înregistrare? Preferabil ar fi să faceți această operațiune într-o interogare SQL. În afară de aceasta, ar trebui să utilizați T-SQL, poate cu ajutorul cursorilor. –  > Por Gordon Linoff.
  • Eu aș folosi un cursor. –  > Por FloChanz.
  • Acest lucru va fi destul de lent – nu este posibil să rescrieți procedura stocată sau să mutați o parte din logică în afara acesteia pentru a lucra într-o manieră bazată pe seturi? –  > Por Bridge.
  • @Funky ce face sproc-ul? Adesea, codul poate fi rescris într-o manieră bazată pe seturi (adică evitarea buclelor). Dacă sunteți ferm convins că doriți să efectuați o operațiune RBAR (simple-talk.com/sql/t-sql-programming/…), atunci un cursor este lucrul pe care doriți să îl investigați. –  > Por gvee.
  • Poate că puteți explica mai detaliat ce veți face cu aceste date. În cele mai multe cazuri, puteți scrie cu ușurință o singură interogare SQL care va face ceea ce aveți nevoie să faceți într-o singură acțiune, în loc să parcurgeți în buclă înregistrările individuale. –  > Por Alan Barber.
8 răspunsuri
FloChanz

Prin utilizarea T-SQL și a cursorilor ca acesta :

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;

Comentarii

  • Lucrul corect este să rescrieți procesul teh astfel încât să nu fie nevoie să se bucle. Looping este o alegere extrem de proastă într-o bază de date. –  > Por HLGEM.
  • 25

  • Poate că aveți dreptate, dar cu informațiile furnizate în întrebare la momentul în care am scris răspunsul, utilizatorul vrea doar să parcurgă în buclă un set de date… iar un cursor este o modalitate de a face acest lucru. –  > Por FloChanz.
  • 18

  • Cursorii sunt doar un instrument – nimic în general corect sau greșit în legătură cu ei. Observați performanța și decideți. Acest răspuns (cursori) este o alegere posibilă. Puteți utiliza, de asemenea, un WHILE LOOP, CTE etc. –  > Por Lanțuri.
  • @FrenkyB Da, puteți. Uitați-vă în acest fel… stackoverflow.com/questions/11035187/… -.  > Por sam yi.
  • Felicitări, soluția ta este chiar pe msdn: msdn.microsoft.com/en-us/library/… și îmi place foarte mult cum folosiți Field Data Type. –  > Por Pete.
sam yi

Asta este ceea ce am făcut eu dacă trebuie să faci ceva iterativ… dar ar fi înțelept să cauți mai întâi operații de set. De asemenea, nu faceți acest lucru pentru că nu doriți să învățați cursori.

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

Comentarii

  • Folosirea unui CURSOR (vezi răspunsul de mai jos) pare a fi o soluție mult mai elegantă. –  > Por Mihail Glukhov.
  • De ce acest răspuns are mai multe voturi în sus decât soluția cu cursor? –  > Por ataravati.
  • 33

  • @ataravati Pentru că această soluție se citește mai curat pentru mulți programatori decât cursorii. Sintaxa pentru cursori este destul de incomodă pentru unii. –  > Por Brian Webster.
  • Acest lucru răspunde de fapt la întrebarea inițială, deoarece vă permite să iterați pe întregul rând; în timp ce cursorul vă permite să iterați pe o anumită coloană dintr-un rând. –  > Por I_do_python.
  • Cursors sunt mai prolixe decât while declarațiile, dar sunt mai optimizate. Nu aș recomanda un while declarație, mai ales cu o subîntrebare în condiția buclei. –  > Por Christiano Kiss.
Precept

Mică modificare la răspunsul lui Sam Yi (pentru o mai bună lizibilitate):

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

Comentarii

  • @bluish, acest răspuns corectează răspunsul lui sam yi. Această corecție este în principal în interiorul select @TableID = (...) afirmație. –  > Por Simple Sandman.
  • Cred că acest răspuns trebuie să fie selectat din această întrebare –  > Por sajadre.
Agnel Amodia

Folosind cursorul puteți cu ușurință să iterați înregistrările individual și să imprimați înregistrările separat sau ca un singur mesaj care să includă toate înregistrările.

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

Comentarii

  • asta pare interesant. Mă întreb ce înseamnă identificatorul @. –  > Por netskink.
  • @ este doar pentru a se diferenția ca variabile. –  > Por Agnel Amodia.
  • Acest lucru este cu siguranță interesant. Dar vă rog să mă lămuriți: —> Cum obține secțiunea „SET @msg …” datele de la client? Și cum sunt câmpurile identificate ca LastName sau FirstName care returnează valoarea? Din ce variabilă? Nu înțeleg această parte, dacă îmi puteți explica, vă rog –  > Por Tsiriniaina Rakotonirina.
Sandeep

Doar o altă abordare dacă nu aveți probleme în a folosi tabele temporare. am testat personal acest lucru și nu va provoca nicio excepție (chiar dacă tabelul temporar nu are date).

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

Comentarii

  • Acest lucru este foarte ciudat. Conține o mulțime de erori, de asemenea, utilizarea a două variabile în care una merge de la 1 la COUNT(*) iar a doua merge de la COUNT(*) la 1 este ciudată. –  > Por David Ferenczy Rogožan.
  • Variabila MAXID este utilizată pentru a face LOOP. Variabila COUNTER este utilizată pentru a efectua o operațiune asupra unei anumite înregistrări din tabel. Dacă citesc întrebarea, se vorbește despre ” am câteva înregistrări prin care doresc să fac o buclă și să fac ceva cu fiecare înregistrare”. S-ar putea să mă înșel, dar vă rog să îmi indicați ce este greșit mai sus @DAWID –  > Por Sandeep.
  • Cred că este evident modul în care folosiți aceste variabile în codul dvs. Puteți avea doar WHILE (@COUTNER <= @ROWID) și nu este nevoie să descrești @ROWID în fiecare iterație. BTW ce se întâmplă dacă ROWIDdin tabelul dvs. nu sunt continue (unele rânduri au fost șterse anterior). –  > Por David Ferenczy Rogožan.
  • Când ați sugera să folosiți un tabel temporar în locul unui cursor? Este doar o alegere de design sau unul dintre ele are performanțe mai bune? –  > Por h0r53.
Bunkerbuster

Ați putea alege să clasificați datele și să adăugați un ROW_NUMBER și să numărați până la zero în timp ce iterați setul de date.

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

Monojit Sarkar

în acest fel putem itera în datele din 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 este o funcție definită de utilizator care va analiza datele separate prin virgulă și va returna tabelul. mulțumesc.

江明哲

Cred că acesta este exemplul simplu de iterare a elementelor.

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