2009-06-25 7 views
38

मेरे पास एक गतिशील SQL कथन है जिसे मैंने संग्रहीत प्रक्रिया में बनाया है। मुझे कर्सर का उपयोग करके परिणामों पर पुन: प्रयास करना होगा। मुझे सही वाक्यविन्यास का पता लगाने में मुश्किल हो रही है। मैं यही कर रहा हूँ।संग्रहीत प्रक्रिया में गतिशील एसक्यूएल के साथ एक कर्सर का उपयोग

SELECT @SQLStatement = 'SELECT userId FROM users' 

DECLARE @UserId 

DECLARE users_cursor CURSOR FOR 
EXECUTE @SQLStatment --Fails here. Doesn''t like this 

OPEN users_cursor 
FETCH NEXT FROM users_cursor 
INTO @UserId 

WHILE @@FETCH_STATUS = 0 
BEGIN 

EXEC asp_DoSomethingStoredProc @UserId 

END 
CLOSE users_cursor 
DEALLOCATE users_cursor 

ऐसा करने का सही तरीका क्या है?

+2

में कर्सर उपयोग की

उदाहरण "सही" जिस तरह से गतिशील एसक्यूएल या कर्सर का उपयोग नहीं करने के लिए है। क्या आप एक वास्तविक उदाहरण दे सकते हैं जिसे आप पूरा करने की कोशिश कर रहे हैं? – NotMe

+0

बिल्कुल - एकल संग्रहित प्रो में SQL सर्वर के दो बड़े NO-NOs :-) –

+1

गतिशील SQL आवश्यक रूप से बुरा नहीं है, खासकर यदि आप EXEC के बजाय sp_executesql का उपयोग करते हैं। कर्सर, हालांकि शापित हैं। – RolandTumble

उत्तर

95

एक कर्सर केवल एक चुनिंदा कथन स्वीकार करेगा, इसलिए यदि SQL को वास्तव में गतिशील होने की आवश्यकता है तो आप जिस कथन का निष्पादन कर रहे हैं उसका कर्सर भाग घोषित करें। नीचे काम करने के लिए आपके सर्वर को वैश्विक कर्सर का उपयोग करना होगा।

Declare @UserID varchar(100) 
declare @sqlstatement nvarchar(4000) 
--move declare cursor into sql to be executed 
set @sqlstatement = 'Declare users_cursor CURSOR FOR SELECT userId FROM users' 

exec sp_executesql @sqlstatement 


OPEN users_cursor 
FETCH NEXT FROM users_cursor 
INTO @UserId 

WHILE @@FETCH_STATUS = 0 
BEGIN 
Print @UserID 
EXEC asp_DoSomethingStoredProc @UserId 

FETCH NEXT FROM users_cursor --have to fetch again within loop 
INTO @UserId 

END 
CLOSE users_cursor 
DEALLOCATE users_cursor 

आप वैश्विक कर्सर का उपयोग कर से बचने की जरूरत है, तो आप भी अपने गतिशील एसक्यूएल के परिणाम अस्थायी तालिका में डालने, और फिर उस तालिका का उपयोग अपने कर्सर को भरने के लिए कर सकता है।

Declare @UserID varchar(100) 
create table #users (UserID varchar(100)) 

declare @sqlstatement nvarchar(4000) 
set @sqlstatement = 'Insert into #users (userID) SELECT userId FROM users' 
exec(@sqlstatement) 

declare users_cursor cursor for Select UserId from #Users 
OPEN users_cursor 
FETCH NEXT FROM users_cursor 
INTO @UserId 

WHILE @@FETCH_STATUS = 0 
BEGIN 

EXEC asp_DoSomethingStoredProc @UserId 

FETCH NEXT FROM users_cursor 
INTO @UserId 

END 
CLOSE users_cursor 
DEALLOCATE users_cursor 

drop table #users 
+0

कर्सर से बचें - वे EVIL हैं! :-) –

+19

कर्सर बुरा नहीं हैं। उत्पादन परिदृश्य में उपयोग के लिए गहन और अव्यवस्थित संसाधन, निश्चित रूप से। बुराई संख्या सिर्फ इसलिए कि कुछ गलत तरीके से इस्तेमाल किया जा सकता है इसका मतलब यह नहीं है कि आपको यह नहीं पता होना चाहिए कि इसका उपयोग कैसे किया जाए। हो सकता है कि मैंने इस सवाल को गलत तरीके से पढ़ा, लेकिन ऐसा लगता है कि कर्सर का उपयोग कैसे करें, न कि उनके उपयोग के लिए पेशेवरों और विपक्ष। – cmsjr

+2

मैं बुराई के साथ जा रहा हूं .... हाँ, ओपी ने एक कर्सर का उपयोग करने के बारे में पूछा। उन्होंने "... ऐसा करने का सही तरीका" के बारे में भी पूछा। जिम्मेदार उत्तर कर्सर के बिना अंतिम परिणाम को पूरा करने के बारे में जानकारी देना है - ऐसा करना लगभग (लगभग) हमेशा संभव है। – RolandTumble

3

सबसे पहले, यदि संभव हो तो कर्सर का उपयोग करने से बचें। यहाँ यह बाहर पक्ष जब यह लगता है कि आप ऐसा नहीं कर सकते के लिए कुछ संसाधन हैं बिना:

There Must Be 15 Ways To Lose Your Cursors... part 1, Introduction

Row-By-Row Processing Without Cursor

कहा कि, हालांकि, आप सभी के बाद एक साथ फंस जा सकता है - मैं डॉन ' यह सुनिश्चित करने के लिए कि आप में से कोई भी लागू नहीं है, अपने प्रश्न से पर्याप्त जानकारी प्राप्त करें। यदि ऐसा है, तो आपको एक अलग समस्या मिली है - आपके कर्सर के लिए चयन कथन वास्तविक चयन कथन होना चाहिए, एक सटीक कथन नहीं। तुम फंसे हो।

लेकिन एक temp तालिका का उपयोग करने के बारे में cmsjr (जो मैं लिख रहा था) से जवाब देखें। मैं वैश्विक कर्सर से "सादे" लोगों को और भी अधिक ....

+2

बस एक एफवाईआई - उन दोनों लेखों को अब आपको देखने के लिए पंजीकरण करने की आवश्यकता है। – tjmoore

+2

दूसरा उदाहरण वास्तव में बुरा है। वह कर्सर का उपयोग करने से बचने के लिए बस एक संग्रहित प्रक्रिया और एक ट्रिगर बनाता है। मुझे संदेह है कि उनका दृष्टिकोण सिर्फ एक सरल आगे कर्सर की तुलना में बेहतर/अधिक प्रदर्शनकारी है। –

4

से बचने था एक ODBC कनेक्शन पर एक गैर संबंधपरक डेटाबेस (IDMS किसी को?) के साथ उत्तीर्ण कार्य उस समय जहां कर्सर और गतिशील एसक्यूएल में से एक के रूप में एकमात्र रास्ता लगता है।

select * from a where a=1 and b in (1,2) 

45 मिनट लगते हैं बिना keysets उपयोग करने के लिए खंड में दूसरा 1 के तहत में चलेंगे फिर से लिखा है, जबकि जवाब करने के लिए:

select * from a where (a=1 and b=1) 
union all 
select * from a where (a=1 and b=2) 

स्तंभ B के लिए बयान में 1145 पंक्तियां हैं, तो एक का उपयोग कर कर्सर को indidivudal स्टेटमेंट बनाने और उन्हें गतिशील एसक्यूएल के रूप में निष्पादित करने के लिए खंड में उपयोग करने से कहीं अधिक तेज है। मूर्खतापूर्ण हे?

और हां, एक संबंधपरक डेटाबेस में कोई समय नहीं है जिसे कर्सर का उपयोग किया जाना चाहिए। मुझे विश्वास नहीं है कि मैं एक उदाहरण में आया हूं जहां एक कर्सर पाश कई आयाम तेज है।

1

हाल ही में ओरेकल से SQL सर्वर (नियोक्ता वरीयता) में स्विच करने के बाद, मुझे लगता है कि SQL सर्वर में कर्सर समर्थन lagging है। कर्सर हमेशा बुराई नहीं होते हैं, कभी-कभी आवश्यक होते हैं, कभी-कभी बहुत तेज़ होते हैं, और कभी-कभी एक जटिल क्वेरी को पुन: व्यवस्थित करने या ऑप्टिमाइज़ेशन संकेत जोड़कर क्लीनर करने की कोशिश करने से साफ करते हैं। एसक्यूएल सर्वर समुदाय में "कर्सर बुरा हैं" राय अधिक महत्वपूर्ण है।

तो मुझे लगता है कि यह जवाब ओरेकल पर स्विच करना है या एमएस को एक सुराग देना है।

14

यह कोड एक कर्सर के साथ गतिशील कॉलम के लिए एक बहुत अच्छा उदाहरण है, क्योंकि आप नहीं कर सकते से '+' में @STATEMENT:

ALTER PROCEDURE dbo.spTEST 
AS 
    SET NOCOUNT ON 
    DECLARE @query NVARCHAR(4000) = N'' --DATA FILTER 
    DECLARE @inputList NVARCHAR(4000) = '' 
    DECLARE @field sysname = '' --COLUMN NAME 
    DECLARE @my_cur CURSOR 
    EXECUTE SP_EXECUTESQL 
     N'SET @my_cur = CURSOR FAST_FORWARD FOR 
      SELECT 
       CASE @field 
        WHEN ''fn'' then fn 
        WHEN ''n_family_name'' then n_family_name 
       END 
      FROM 
       dbo.vCard 
      WHERE 
       CASE @field 
        WHEN ''fn'' then fn 
        WHEN ''n_family_name'' then n_family_name 
       END 
       LIKE ''%''[email protected]+''%''; 
      OPEN @my_cur;', 
     N'@field sysname, @query NVARCHAR(4000), @my_cur CURSOR OUTPUT', 
     @field = @field, 
     @query = @query, 
     @my_cur = @my_cur OUTPUT 
    FETCH NEXT FROM @my_cur INTO @inputList 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     PRINT @inputList 
     FETCH NEXT FROM @my_cur INTO @inputList 
    END 
    RETURN 
+0

यह मेरे लिए काम करता है, और वैश्विक कर्सर और अस्थायी तालिकाओं से बचाता है। धन्यवाद – Dale

+0

बिल्कुल सही, बहुत उपयोगी धन्यवाद! एक नोट के रूप में, ऐसा लगता है कि कर्सर को गतिशील एसक्यूएल के भीतर भी खोला जाना चाहिए - SQL सर्वर को लगता है कि कर्सर प्रारंभ नहीं हुआ है, और गतिशील सामग्री के बाद किए गए मेरे लिए एक त्रुटि फेंक रहा था। –

+0

मैं नहीं देख सकता कि एसक्यूएल में 'क्वेरी' को कैसे जोड़ना है यहां काम करने जा रहा है। मुझे लगता है कि आपको 'LIKE'% 'xx'% 'प्राप्त करने जा रहे हैं; 'मान लीजिए कि' @ query' 'xx'' पर सेट है, जो अमान्य एसक्यूएल है। और SP_EXECUTESQL स्थिति के लिए दस्तावेज़: "@stmt या तो यूनिकोड स्थिर या यूनिकोड चर होना चाहिए।", जिसका अर्थ है कि आप स्ट्रिंग का निर्माण नहीं कर सकते हैं और अस्थायी चर के बिना इसे निष्पादित कर सकते हैं। –

-2

यह कोड आपके लिए उपयोगी हो सकता है। एसक्यूएल सर्वर

DECLARE sampleCursor CURSOR FOR 
     SELECT K.Id FROM TableA K WHERE ....; 
OPEN sampleCursor 
FETCH NEXT FROM sampleCursor INTO @Id 
WHILE @@FETCH_STATUS <> -1 
BEGIN 

UPDATE TableB 
    SET 
     ... 
संबंधित मुद्दे