SQL सर्वर लूप - मैं रिकॉर्ड के एक सेट के माध्यम से कैसे लूप करता हूं


151

मैं चयन से रिकॉर्ड के एक सेट के माध्यम से कैसे लूप कर सकता हूं?

इसलिए उदाहरण के लिए कहूं कि मेरे पास कुछ रिकॉर्ड हैं जिन्हें मैं हर रिकॉर्ड के साथ लूप करना चाहता हूं और कुछ करना चाहता हूं। यहाँ मेरे चयन का एक आदिम संस्करण है:

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

धन्यवाद


5
आप प्रत्येक रिकॉर्ड के लिए क्या करना चाहते हैं? प्राथमिकता SQL क्वेरी में कार्य करने की होगी। यह कहते हुए कि आपको शायद टी-एसक्यूएल का उपयोग करने की आवश्यकता है, शायद श्राप देने वालों के साथ।
गॉर्डन लिनोफ़

2
मैं एक कर्सर का उपयोग करेगा।
फ़्लचेंज

5
यह काफी धीमी गति से होगा - क्या संग्रहित खरीद को फिर से लिखना या सेट-आधारित तरीके से काम करने के लिए इसमें से कुछ तर्क को स्थानांतरित करना संभव नहीं है?
ब्रिज

2
@ फंकी क्या करता है स्पोक? अक्सर कोड को एक सेट आधारित तरीके से लिखा जा सकता है (यानी लूप से बचें)। यदि आप अड़े हैं तो आप RBAR ऑपरेशन ( simple-talk.com/sql/t-sql-programming/… ) करना चाहते हैं तो एक कर्सर वह चीज़ है जिसकी आप जाँच करना चाहते हैं।
१५

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
सही बात यह है कि तेह प्रक्रिया को फिर से लिखना है ताकि इसे लूप करने की आवश्यकता न हो। डेटाबेस में लूपिंग एक बेहद खराब विकल्प है।
एचएलजीईएम

23
शायद आप सही हैं लेकिन उस समय की गई जानकारी के साथ जब मैंने उत्तर लिखा था कि उपयोगकर्ता केवल डेटा के एक सेट के माध्यम से लूप करना चाहता है ... और एक कर्सर इसे करने का एक तरीका है।
फ्लोचनज

16
कर्सर केवल एक उपकरण है - उनके बारे में आम तौर पर सही या गलत कुछ भी नहीं। प्रदर्शन का निरीक्षण करें और निर्णय लें। यह उत्तर (अभिशाप) एक संभव विकल्प है। आप WHILE LOOP, CTE, आदि का उपयोग भी कर सकते हैं
चेन

2
@FrenkyB हाँ आप कर सकते हैं। इस तरह से देखें ... stackoverflow.com/questions/11035187/…
sam yi

2
बधाई हो, आपका समाधान msdn पर भी है: msdn.microsoft.com/en-us/library/… और मुझे वास्तव में पसंद है कि आप फ़ील्ड डेटा प्रकार का उपयोग कैसे करते हैं।
पीट

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 का उपयोग करना (नीचे उत्तर देखें) एक बहुत अधिक सुरुचिपूर्ण समाधान प्रतीत होता है।
मिखाइल ग्लूखोव

इस जवाब में कर्सर समाधान से अधिक अप-वोट क्यों हैं?
अरावती

29
@ कतरवती क्योंकि यह समाधान कई प्रोग्रामरों को अधिक सफाई से पढ़ता है, क्योंकि वे शाप देते हैं। कर्सर के लिए वाक्य रचना कुछ के लिए अजीब है।
ब्रायन वेबस्टर

धन्यवाद! उपरोक्त कोड का उपयोग करके तर्क द्वारा अद्यतन और समूह के साथ मेरा उदाहरण: pastebin.com/GAjUNNi9 । शायद किसी के लिए उपयोगी होगा।
निग्रिमिस्ट

क्या लूप के अंदर अपडेट स्टेटमेंट में कॉलम को नाम के रूप में इस्तेमाल किया जा सकता है? "टेबलनेम अपडेट करें @ 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 = (...)कथन के अंदर है ।
सिंपल सैंडमैन

मुझे लगता है कि इस उत्तर को इस प्रश्न का चयन करना होगा
सज्जादे

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
यह दिलचस्प लग रहा है। मुझे आश्चर्य है कि @ पहचानकर्ता का क्या मतलब है।
नेटस्किंक

@ केवल चर के रूप में अंतर करना है।
एगेल अमोदिया

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 COUNT(*)और दूसरे से COUNT(*)1 तक जाना अजीब है।
डेविड फेरेंस्की रोगोजान

चर MAXID का उपयोग LOOP के माध्यम से किया जाता है। तालिका में किसी विशेष रिकॉर्ड पर कार्रवाई करने के लिए चर COUNTER का उपयोग किया जाता है। अगर मैं इस प्रश्न के बारे में पढ़ता हूं तो "कुछ रिकॉर्ड हैं जिन्हें मैं चाहता हूं कि मैं प्रत्येक रिकॉर्ड के साथ कुछ करूं और कुछ करूं"। मैं गलत हो सकता हूं लेकिन कृपया यह बताएं कि @DAWID के ऊपर क्या गलत है
संदीप

2
मुझे लगता है कि यह स्पष्ट है कि आप अपने कोड में उन चर का उपयोग कैसे करते हैं। आपके पास बस हो सकता है WHILE (@COUTNER <= @ROWID)और आपको @ROWIDप्रत्येक पुनरावृत्ति में कमी करने की आवश्यकता नहीं है । यदि ROWIDआपकी तालिका में s निरंतर नहीं है तो BTW क्या होता है (कुछ पंक्तियों को पहले हटा दिया गया था)।
डेविड फेरेंस्की रोगोजान

1
आप एक कर्सर का उपयोग कर एक अस्थायी तालिका का उपयोग करने का सुझाव कब देंगे? क्या यह केवल एक डिज़ाइन विकल्प है, या किसी का प्रदर्शन बेहतर है?
hrr53

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.