टी-एसक्यूएल: ज्ञात मूल्यों की एक सरणी के माध्यम से लूपिंग


90

यहाँ मेरा परिदृश्य है:

मान लीजिए कि मेरे पास एक संग्रहीत प्रक्रिया है जिसमें मुझे विशिष्ट आईडी के सेट पर एक और संग्रहीत प्रक्रिया को कॉल करने की आवश्यकता है; क्या इसे करने का कोई तरीका है?

इसके बजाय ऐसा करने की आवश्यकता है:

exec p_MyInnerProcedure 4
exec p_MyInnerProcedure 7
exec p_MyInnerProcedure 12
exec p_MyInnerProcedure 22
exec p_MyInnerProcedure 19

कुछ इस तरह से करना:

*magic where I specify my list contains 4,7,12,22,19*

DECLARE my_cursor CURSOR FAST_FORWARD FOR
*magic select*

OPEN my_cursor 
FETCH NEXT FROM my_cursor INTO @MyId
WHILE @@FETCH_STATUS = 0
BEGIN

exec p_MyInnerProcedure @MyId

FETCH NEXT FROM my_cursor INTO @MyId
END

यहाँ मेरा मुख्य लक्ष्य केवल स्थिरता है (व्यवसाय में परिवर्तन के रूप में आईडी को हटाना / जोड़ना आसान है), एक ही लाइन पर सभी ईद को सूचीबद्ध करने में सक्षम होने के नाते ... प्रदर्शन एक मुद्दे के रूप में बड़ा नहीं होना चाहिए


संबंधित, अगर आपको नॉन-पूर्णांक सूची जैसे कि varchars, कर्सर के साथ समाधान पर पुनरावृति करने की आवश्यकता है: iterate-through-a-list-of-strings-in-sql-server
Pac0

जवाबों:


105
declare @ids table(idx int identity(1,1), id int)

insert into @ids (id)
    select 4 union
    select 7 union
    select 12 union
    select 22 union
    select 19

declare @i int
declare @cnt int

select @i = min(idx) - 1, @cnt = max(idx) from @ids

while @i < @cnt
begin
     select @i = @i + 1

     declare @id = select id from @ids where idx = @i

     exec p_MyInnerProcedure @id
end

मैं उम्मीद कर रहा था कि एक और अधिक सुंदर तरीका होगा, लेकिन मुझे लगता है कि यह उतना ही करीब होगा जितना मैं प्राप्त कर सकता हूं: यहां चयनित / यूनियनों और उदाहरण से कर्सर का उपयोग करने के बीच एक हाइब्रिड का उपयोग करके समाप्त हुआ। धन्यवाद!
जॉन

13
@ जॉन: यदि आप 2008 का उपयोग कर रहे हैं, तो आप INSERT @ids VALUES (4), (7), (12), (22), (19)
पीटर रैडोचिया

2
सिर्फ FYI करें, इस तरह की मेमोरी टेबल आम तौर पर कर्सर से तेज होती हैं (हालांकि 5 मानों के लिए मैं शायद ही कोई फर्क पड़ता देख पाऊं), लेकिन मुझे उनकी पसंद का सबसे बड़ा कारण यह है कि मुझे सिंटैक्स वही मिलता है जो आपको एप्लीकेशन कोड में मिलेगा। , जबकि कर्सर (मेरे लिए) अपेक्षाकृत अलग दिखाई देते हैं।
एडम रॉबिन्सन

हालांकि, यह केवल बहुत कम प्रदर्शन को ही नुकसान पहुंचाएगा, लेकिन मैं यह बताना चाहता हूं कि यह परिभाषित स्थान के भीतर सभी नंबरों से होता है। नीचे मौजूद समाधान के साथ मौजूद है (Select @ From @Ids) ... तार्किक रूप से अधिक ध्वनि (और अधिक सुरुचिपूर्ण) है।
डेर यू

41

इस परिदृश्य में मैं क्या करता हूं Ids धारण करने के लिए एक तालिका चर बनाता है।

  Declare @Ids Table (id integer primary Key not null)
  Insert @Ids(id) values (4),(7),(12),(22),(19)

- (या इस तालिका को जनरेट करने के लिए किसी अन्य तालिका मूल्यवान फ़ंक्शन को कॉल करें)

फिर इस तालिका में पंक्तियों के आधार पर लूप

  Declare @Id Integer
  While exists (Select * From @Ids)
    Begin
      Select @Id = Min(id) from @Ids
      exec p_MyInnerProcedure @Id 
      Delete from @Ids Where id = @Id
    End

या ...

  Declare @Id Integer = 0 -- assuming all Ids are > 0
  While exists (Select * From @Ids
                where id > @Id)
    Begin
      Select @Id = Min(id) 
      from @Ids Where id > @Id
      exec p_MyInnerProcedure @Id 
    End

या तो उपरोक्त दृष्टिकोण एक कर्सर (नियमित उपयोगकर्ता तालिका (ओं) के खिलाफ घोषित) की तुलना में बहुत तेज है। तालिका-मूल्यवान चरों का बुरा दोहराव होता है क्योंकि जब अनुचित तरीके से उपयोग किया जाता है, (बड़ी संख्या में पंक्तियों के साथ बहुत विस्तृत तालिकाओं के लिए) तो वे प्रदर्शन करने वाले नहीं होते हैं। लेकिन अगर आप उन्हें केवल एक प्रमुख मूल्य या एक 4 बाइट पूर्णांक रखने के लिए उपयोग कर रहे हैं, तो एक सूचकांक (जैसा कि इस मामले में) वे बहुत तेज़ हैं।


उपरोक्त दृष्टिकोण टेबल चर पर घोषित कर्सर की तुलना में धीमा या धीमा है। यह निश्चित रूप से तेज नहीं है। यह नियमित उपयोगकर्ता तालिकाओं पर w / डिफ़ॉल्ट विकल्प घोषित किए गए कर्सर से अधिक तेज़ होगा, हालाँकि।
बजे पीटर रैडोचिया

@ पेटर, आह, हाँ, आप सही हैं, मैं गलत तरीके से मानता हूं कि एक कर्सर का उपयोग एक नियमित उपयोगकर्ता तालिका का उपयोग करता है, न कि एक टेबल चर .. Ihave ने भेद स्पष्ट करने के लिए संपादित किया
चार्ल्स ब्रेटाना

16

एक स्थिर कर्सर चर और एक स्प्लिट फ़ंक्शन का उपयोग करें :

declare @comma_delimited_list varchar(4000)
set @comma_delimited_list = '4,7,12,22,19'

declare @cursor cursor
set @cursor = cursor static for 
  select convert(int, Value) as Id from dbo.Split(@comma_delimited_list) a

declare @id int
open @cursor
while 1=1 begin
  fetch next from @cursor into @id
  if @@fetch_status <> 0 break
  ....do something....
end
-- not strictly necessary w/ cursor variables since they will go out of scope like a normal var
close @cursor
deallocate @cursor

उपयोगकर्ता तालिकाओं के विरुद्ध घोषित किए जाने पर डिफ़ॉल्ट विकल्प के बाद से कर्सर का खराब प्रतिनिधि होता है जो बहुत अधिक ओवरहेड उत्पन्न कर सकता है।

लेकिन इस मामले में ओवरहेड काफी न्यूनतम है, यहां किसी भी अन्य तरीकों से कम है। STATIC SQL सर्वर को tempdb में परिणाम और फिर उस पर पुनरावृत्ति करने के लिए कहता है। इस तरह की छोटी सूचियों के लिए, यह इष्टतम समाधान है।


7

आप नीचे के रूप में कोशिश कर सकते हैं:

declare @list varchar(MAX), @i int
select @i=0, @list ='4,7,12,22,19,'

while( @i < LEN(@list))
begin
    declare @item varchar(MAX)
    SELECT  @item = SUBSTRING(@list,  @i,CHARINDEX(',',@list,@i)-@i)
    select @item

     --do your stuff here with @item 
     exec p_MyInnerProcedure @item 

    set @i = CHARINDEX(',',@list,@i)+1
    if(@i = 0) set @i = LEN(@list) 
end

6
मैं उस सूची की घोषणा इस तरह से करूँगा: @list ='4,7,12,22,19' + ','- इसलिए यह पूरी तरह से स्पष्ट है कि सूची को अल्पविराम के साथ समाप्त होना है (यह इसके बिना काम करेगा!)।
AjV Jsy

5

मैं आमतौर पर निम्नलिखित दृष्टिकोण का उपयोग करता हूं

DECLARE @calls TABLE (
    id INT IDENTITY(1,1)
    ,parameter INT
    )

INSERT INTO @calls
select parameter from some_table where some_condition -- here you populate your parameters

declare @i int
declare @n int
declare @myId int
select @i = min(id), @n = max(id) from @calls
while @i <= @n
begin
    select 
        @myId = parameter
    from 
        @calls
    where id = @i

        EXECUTE p_MyInnerProcedure @myId
    set @i = @i+1
end

2
CREATE TABLE #ListOfIDs (IDValue INT)

DECLARE @IDs VARCHAR(50), @ID VARCHAR(5)
SET @IDs = @OriginalListOfIDs + ','

WHILE LEN(@IDs) > 1
BEGIN
SET @ID = SUBSTRING(@IDs, 0, CHARINDEX(',', @IDs));
INSERT INTO #ListOfIDs (IDValue) VALUES(@ID);
SET @IDs = REPLACE(',' + @IDs, ',' + @ID + ',', '')
END

SELECT * 
FROM #ListOfIDs

0

एक प्रक्रियात्मक प्रोग्रामिंग भाषा (यहां पायथन) का उपयोग करके अपने DB से संबंध बनाएं, और वहां लूप करें। इस तरह से आप जटिल लूप भी कर सकते हैं।

# make a connection to your db
import pyodbc
conn = pyodbc.connect('''
                        Driver={ODBC Driver 13 for SQL Server};
                        Server=serverName;
                        Database=DBname;
                        UID=userName;
                        PWD=password;
                      ''')
cursor = conn.cursor()

# run sql code
for id in [4, 7, 12, 22, 19]:
  cursor.execute('''
    exec p_MyInnerProcedure {}
  '''.format(id))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.