2008-10-21 7 views
21

मैं एक संग्रहीत प्रक्रिया लिख ​​रहा हूं जिसमें इसमें बहुत सारी कंडीशनिंग की आवश्यकता है। सी # .NET कोडिंग से सामान्य ज्ञान के साथ अपवाद प्रदर्शन को नुकसान पहुंचा सकता है, मैंने हमेशा पीएल/एसक्यूएल में उनका उपयोग करने से बचा है।ओरेकल पीएल/एसक्यूएल - संग्रहित प्रक्रिया प्रदर्शन के लिए NO_DATA_FOUND अपवाद खराब हैं?

SELECT COUNT(*) INTO var WHERE condition; 
IF var > 0 THEN 
    SELECT NEEDED_FIELD INTO otherVar WHERE condition; 
.... 

-या-

SELECT NEEDED_FIELD INTO var WHERE condition; 
EXCEPTION 
WHEN NO_DATA_FOUND 
.... 

दूसरे मामले में थोड़ा लगता है: यह संग्रहीत proc में मेरे कंडीशनिंग ज्यादातर के चारों ओर एक रिकॉर्ड मौजूद है या नहीं, जो मैं कर सकता दो तरह से घूमती मेरे लिए अधिक सुरुचिपूर्ण, क्योंकि तब मैं NEEDED_FIELD का उपयोग कर सकता हूं, जिसे मुझे पहले मामले में शर्त के बाद पहले कथन में चयन करना होगा। कम कोड लेकिन अगर संग्रहित प्रक्रिया COUNT (*) का उपयोग करके तेज़ी से चलती है, तो मुझे प्रसंस्करण गति को बनाने के लिए थोड़ा और टाइप करना बुरा नहीं लगता है।

कोई संकेत? क्या मुझे एक और संभावना याद आ रही है?

EDIT मुझे यह उल्लेख करना चाहिए था कि यह सब पहले से ही एक लूप में घोंसला है। निश्चित नहीं है कि यह कर्सर का उपयोग करने में कोई फर्क पड़ता है, क्योंकि मुझे नहीं लगता कि मैं कर्सर को फॉर लूप में चुनने के रूप में अस्वीकार कर सकता हूं।

उत्तर

28

मैं ऐसा करने के लिए एक स्पष्ट कर्सर का उपयोग नहीं करता। स्टीव एफ अब लोगों को स्पष्ट कर्सर का उपयोग करने की सलाह नहीं देता है जब एक निहित कर्सर का उपयोग किया जा सकता है।

count(*) के साथ विधि असुरक्षित है। यदि कोई अन्य सत्र उस पंक्ति को हटा देता है जो count(*) के साथ लाइन के बाद की स्थिति से मेल खाता है, और select ... into के साथ लाइन से पहले, कोड एक अपवाद फेंक देगा जो संभाला नहीं जाएगा।

मूल पोस्ट के दूसरे संस्करण में यह समस्या नहीं है, और इसे आम तौर पर पसंद किया जाता है।

उस ने कहा, अपवाद का उपयोग करके एक मामूली ओवरहेड है, और यदि आप 100% सुनिश्चित हैं कि डेटा नहीं बदलेगा, तो आप count(*) का उपयोग कर सकते हैं, लेकिन मैं इसके खिलाफ अनुशंसा करता हूं।

मैं पर 32 बिट Windowsओरेकल 10.2.0.1 पर इन मील के पत्थरों भाग गया। मैं केवल समय बीत रहा हूँ। अन्य परीक्षण harnesses हैं जो अधिक जानकारी दे सकते हैं (जैसे लच गणना और स्मृति इस्तेमाल)।

SQL>create table t (NEEDED_FIELD number, COND number); 

टेबल बनाया।

SQL>insert into t (NEEDED_FIELD, cond) values (1, 0); 

1 पंक्ति बनाई है।

declare 
    otherVar number; 
    cnt number; 
begin 
    for i in 1 .. 50000 loop 
    select count(*) into cnt from t where cond = 1; 

    if (cnt = 1) then 
     select NEEDED_FIELD INTO otherVar from t where cond = 1; 
    else 
     otherVar := 0; 
    end if; 
    end loop; 
end; 
/

PL/SQL प्रक्रिया सफलतापूर्वक पूर्ण हुआ।

बीत चुके: 00: 00: 02,70

declare 
    otherVar number; 
begin 
    for i in 1 .. 50000 loop 
    begin 
     select NEEDED_FIELD INTO otherVar from t where cond = 1; 
    exception 
     when no_data_found then 
     otherVar := 0; 
    end; 
    end loop; 
end; 
/

PL/SQL प्रक्रिया सफलतापूर्वक पूर्ण हुआ।

बीत चुके: 00: 00: 03,06

1

कर्सर का उपयोग कर हाँ, आप भूल रहे हैं

DECLARE 
    CURSOR foo_cur IS 
    SELECT NEEDED_FIELD WHERE condition ; 
BEGIN 
    OPEN foo_cur; 
    FETCH foo_cur INTO foo_rec; 
    IF foo_cur%FOUND THEN 
    ... 
    END IF; 
    CLOSE foo_cur; 
EXCEPTION 
    WHEN OTHERS THEN 
    CLOSE foo_cur; 
    RAISE; 
END ; 

बेशक यह अधिक कोड है, लेकिन यह प्रवाह नियंत्रण जो, स्टीव Feuerstein के पी एल से मेरी PL/SQL के सबसे सीखा होने के रूप में अपवाद का उपयोग नहीं करता/एसक्यूएल प्रोग्रामिंग पुस्तक, मैं एक अच्छी बात मानता हूं।

चाहे यह तेज़ है या नहीं, मुझे नहीं पता (मैं आजकल बहुत कम पीएल/एसक्यूएल करता हूं)।

+0

धन्यवाद, स्टीव। ऊपर मेरा संपादन देखें। क्या इससे कोई फर्क पड़ता है? –

+0

ओह, दुह! बेशक यह काम करेगा। ठीक है, और कॉफी की जरूरत है। धन्यवाद। बेशक –

2

यदि यह महत्वपूर्ण है तो आपको वास्तव में दोनों विकल्पों को बेंचमार्क करना होगा!

ऐसा कहकर, मैंने हमेशा अपवाद विधि का उपयोग किया है, तर्क यह है कि केवल एक बार डेटाबेस को हिट करना बेहतर होगा।

6

@ स्टीव के कोड का एक विकल्प।

DECLARE 
    CURSOR foo_cur IS 
    SELECT NEEDED_FIELD WHERE condition ; 
BEGIN 
    FOR foo_rec IN foo_cur LOOP 
    ... 
    END LOOP; 
EXCEPTION 
    WHEN OTHERS THEN 
    RAISE; 
END ; 

यदि कोई डेटा नहीं है तो लूप निष्पादित नहीं किया जाता है। लूप के लिए कर्सर जाने का रास्ता है - वे बहुत सारे हाउसकीपिंग से बचने में मदद करते हैं। एक और अधिक कॉम्पैक्ट समाधान:

DECLARE 
BEGIN 
    FOR foo_rec IN (SELECT NEEDED_FIELD WHERE condition) LOOP 
    ... 
    END LOOP; 
EXCEPTION 
    WHEN OTHERS THEN 
    RAISE; 
END ; 

यदि आप संकलन समय पर पूर्ण चयन कथन को जानते हैं तो कौन सा काम करता है।

4

@DCookie

मैं बस का कहना है कि आप लाइनों का कहना है कि

EXCEPTION 
    WHEN OTHERS THEN  
    RAISE; 

आप करेंगे बंद छोड़ सकते हैं चाहता हूँ यदि आप अपवाद ब्लॉक को एक साथ छोड़ देते हैं, तो वही प्रभाव प्राप्त करें, और अपवाद के लिए रिपोर्ट की गई लाइन संख्या वह रेखा होगी जहां अपवाद वास्तव में फेंक दिया गया है, न कि एक्सेज में लाइन पशन ब्लॉक जहां इसे फिर से उठाया गया था।

+0

। मैंने इसे अभी छोड़ा क्योंकि यह फोर लूप के अंदर क्या कर रहा है और अपवाद हैंडलर में आपने जो भी किया है उसके आधार पर उपयोगी हो सकता है। – DCookie

3

स्टीफन Darlington एक बहुत अच्छा बिंदु बनाता है, और आप देख सकते हैं अगर आप मेरी बेंचमार्क बदल कि एक अधिक वास्तविक आकार तालिका का उपयोग करने अगर मैं 10000 पंक्तियों निम्नलिखित का उपयोग कर के लिए बाहर तालिका भरने: फिर

begin 
    for i in 2 .. 10000 loop 
    insert into t (NEEDED_FIELD, cond) values (i, 10); 
    end loop; 
end; 

बेंचमार्क फिर से चलाएं। (मुझे उचित समय प्राप्त करने के लिए लूप की संख्या 5000 तक कम करना पड़ा)।

declare 
    otherVar number; 
    cnt number; 
begin 
    for i in 1 .. 5000 loop 
    select count(*) into cnt from t where cond = 0; 

    if (cnt = 1) then 
     select NEEDED_FIELD INTO otherVar from t where cond = 0; 
    else 
     otherVar := 0; 
    end if; 
    end loop; 
end; 
/

PL/SQL procedure successfully completed. 

Elapsed: 00:00:04.34 

declare 
    otherVar number; 
begin 
    for i in 1 .. 5000 loop 
    begin 
     select NEEDED_FIELD INTO otherVar from t where cond = 0; 
    exception 
     when no_data_found then 
     otherVar := 0; 
    end; 
    end loop; 
end; 
/

PL/SQL procedure successfully completed. 

Elapsed: 00:00:02.10 

अपवाद के साथ विधि अब दो गुना से अधिक है। तो, लगभग सभी मामलों के लिए, विधि:

SELECT NEEDED_FIELD INTO var WHERE condition; 
EXCEPTION 
WHEN NO_DATA_FOUND.... 

जाने का रास्ता है। यह सही परिणाम देगा और आम तौर पर सबसे तेज़ है।

0

यहाँ एक मरे हुए घोड़े की धड़कन हो सकता है, लेकिन मैं पाश के लिए कर्सर बेंच-चिह्नित है, और उस no_data_found पद्धति के रूप में रूप में अच्छी तरह के बारे में प्रदर्शन किया:

declare 
    otherVar number; 
begin 
    for i in 1 .. 5000 loop 
    begin 
     for foo_rec in (select NEEDED_FIELD from t where cond = 0) loop 
     otherVar := foo_rec.NEEDED_FIELD; 
     end loop; 
     otherVar := 0; 
    end; 
    end loop; 
end; 

PL/SQL प्रक्रिया सफलतापूर्वक पूर्ण हुआ।

बीत चुके: 00: 00:

SELECT MAX(column) 
    INTO var 
    FROM table 
WHERE conditions; 

IF var IS NOT NULL 
THEN ... 

का चयन करें आप मूल्य दे देंगे: 02,18

7

के बाद से चयन जांच मानता है कि एक ही पंक्ति लौटा दी जाएगी, तो आप फार्म के एक बयान का उपयोग कर सकते यदि कोई उपलब्ध है, और NO_DATA_FOUND अपवाद के बजाय NULL का मान है। MAX() द्वारा पेश किया गया ओवरहेड न्यूनतम-से-शून्य होगा क्योंकि परिणाम सेट में एक पंक्ति होती है। इसे कर्सर-आधारित समाधान के सापेक्ष कॉम्पैक्ट होने का लाभ भी है, और मूल पोस्ट में दो-चरण समाधान जैसे समवर्ती मुद्दों के प्रति संवेदनशील नहीं है।

+2

इस समाधान के साथ नुकसान यह है कि यह अन्य अपवाद मामलों को छुपाएगा जिन्हें आप छिपाना नहीं चाहते हैं क्योंकि ऐसा नहीं होना चाहिए, जैसे TOO_MANY_ROWS अपवाद। – pauloya

1

नेस्टेड कर्सर लूप होने के बजाय टेबल के बीच बाहरी जुड़ने के साथ एक कर्सर लूप का उपयोग करने के लिए एक और अधिक कुशल दृष्टिकोण होगा।

BEGIN 
    FOR rec IN (SELECT a.needed_field,b.other_field 
        FROM table1 a 
        LEFT OUTER JOIN table2 b 
        ON a.needed_field = b.condition_field 
       WHERE a.column = ???) 
    LOOP 
     IF rec.other_field IS NOT NULL THEN 
     -- whatever processing needs to be done to other_field 
     END IF; 
    END LOOP; 
END; 
+0

यह निश्चित रूप से बेहतर दृष्टिकोण है क्योंकि आप एक अलग SQL कथन से बचते हैं। ओरेकल बाहरी जुड़ने के चयन को बेहतर ढंग से अनुकूलित कर सकता है क्योंकि यह जानता है कि आप तालिका 1 में प्रत्येक पंक्ति के लिए क्या कर रहे हैं। –

+0

इस उदाहरण में बाहरी जुड़ने को आंतरिक जुड़ने और आईएफ स्थिति को हटाने के लिए अभी भी बेहतर होगा। –

0

जब आप लूप के लिए उपयोग कर रहे हों तो आपको खुले उपयोग का उपयोग नहीं करना पड़ेगा। शून्य, कोई बात नहीं क्या -

declare 
cursor cur_name is select * from emp; 
begin 
for cur_rec in cur_name Loop 
    dbms_output.put_line(cur_rec.ename); 
end loop; 
End ; 

या

declare 
cursor cur_name is select * from emp; 
cur_rec emp%rowtype; 
begin 
Open cur_name; 
Loop 
Fetch cur_name into Cur_rec; 
    Exit when cur_name%notfound; 
    dbms_output.put_line(cur_rec.ename); 
end loop; 
Close cur_name; 
End ; 
0

गिनती (*), क्योंकि यह हमेशा वास्तविक गणना या 0 रिटर्न अपवाद उठाना कभी नहीं होगा। मैं गिनती का उपयोग करता हूँ।

0

पहले (उत्कृष्ट) जवाब में कहा गया है -

गिनती के साथ विधि() असुरक्षित है। यदि कोई अन्य सत्र उस पंक्ति को हटा देता है जो गिनती (*) के साथ लाइन के बाद की स्थिति से मिलता है, और चयन के साथ लाइन से पहले ... में, कोड एक अपवाद फेंक देगा जो संभाला नहीं जाएगा।

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

+0

यह केवल तभी सच है जब अलगाव_लेवल को क्रमबद्ध करने के लिए सेट किया गया हो। –

संबंधित मुद्दे