2010-09-06 19 views
7

कहें कि मेरे पास नमूना तालिका है:एसक्यूएल क्वेरी में वैकल्पिक पैरामीटर कैसे संभालें?

 id_pk value 
------------ 
1  a 
2  b 
3  c 

और मेरे पास एक नमूना पीएल/एसक्यूएल ब्लॉक है, जिसमें एक क्वेरी है जो वर्तमान में एक पंक्ति में एक पंक्ति का चयन करती है:

declare 

    type t_table is table of myTable%rowtype; 

    n_RequiredId myTable.id_pk%type := 1; 
    t_Output t_table := t_table(); 

begin 

    select m.id_pk, m.value 
    bulk collect into t_Output 
    from myTable m 
    where m.id_pk = n_RequiredId; 

end; 

मुझे क्या चाहिए यदि n_RequiredID, जो वास्तव में उपयोगकर्ता-इनपुट पैरामीटर है, null पर सेट है, तो उपरोक्त ब्लॉक में दिखाए गए अनुसार, किसी सरणी में एक पंक्ति को चुनने के लिए या को सरणी में एक पंक्ति का चयन करने की क्षमता लागू करना है।

और, सवाल यह है कि ऐसी स्थिति को संभालने का सबसे अच्छा अभ्यास क्या है?

मैं अपनी क्वेरी के where खंड को इस तरह से कुछ संशोधित करने के बारे में सोच सकता हूं:

where m.id_pk = nvl(n_RequiredId, m.id_pk); 

लेकिन मुझे लगता है कि अगर पैरामीटर शून्य नहीं होगा, तो मुझे क्वेरी धीमा करने जा रहा है, और मुझे याद है कि क्यूटे ने कहा इस दृष्टिकोण के बारे में कुछ वास्तव में बुरा है।

मैं निम्नलिखित पीएल/एसक्यूएल तर्क को लागू करने के बारे में भी सोच सकता हूं:

if n_RequiredId is null then 

    select m.id_pk, m.value bulk collect into t_Output from myTable m; 

else 

    select m.id_pk, m.value bulk collect 
    into t_Output 
    from myTable m 
    where m.id_pk = n_RequiredId; 

end if; 

लेकिन अगर मुझे इस तरह के एक से अधिक पैरामीटर का सामना करना पड़ता है तो यह बहुत जटिल हो जाएगा।

आप मुझे क्या सलाह देंगे?

उत्तर

14

हाँ, निम्न में से किसी का उपयोग कर:

WHERE m.id_pk = NVL(n_RequiredId, m.id_pk); 
WHERE m.id_pk = COALESCE(n_RequiredId, m.id_pk); 
WHERE (n_RequiredId IS NULL OR m.id_pk = n_RequiredId); 

... not sargable हैं। वे काम करेंगे, लेकिन उपलब्ध विकल्पों में से सबसे खराब प्रदर्शन करेंगे।

यदि आपके पास केवल एक पैरामीटर है, तो IF/ELSE और अलग, अनुरूप कथन बेहतर विकल्प हैं।

उसके बाद अगला विकल्प dynamic SQL है। लेकिन गतिशील एसक्यूएल कोडिंग बेकार है यदि आप पहले उदाहरण में गैर-सार योग्य भविष्यवाणियों को लेते हैं। डायनामिक एसक्यूएल आपको कई पथों को समायोजित करते समय क्वेरी को तैयार करने की अनुमति देता है। लेकिन यह भी, एसक्यूएल इंजेक्शन जोखिम तो यह संकुल में संग्रहित प्रक्रियाओं/कार्य के भीतर (पैरामिट्रीकृत प्रश्नों के पीछे प्रदर्शन किया जाना चाहिए अधिमानतः।

+0

ओह महान! यह मेरा दिन बचाता है। हालांकि, कॉलम शून्य होने पर पहला व्यक्ति coz काम नहीं करता है, यह खंड शून्य मानों को फ़िल्टर करेगा जबकि प्रश्न में आप दूसरे मामले में शून्य मान फ़िल्टर नहीं करेंगे। –

4

OMG_Ponies 'और रोब वैन Wijk के जवाब पूरी तरह से सही हैं, यह सिर्फ पूरक है।

नहीं है बाध्य चर का उपयोग करना आसान बनाने के लिए एक अच्छी चाल है और अभी भी गतिशील एसक्यूएल का उपयोग करें। यदि आप सभी बाइंड्स को शुरुआत में क्लॉज में डाल देते हैं, तो आप हमेशा चर के उसी सेट को बांध सकते हैं, चाहे आप जा रहे हों या नहीं उनका उपयोग करें।

उदाहरण के लिए, कहें कि आपके पास तीन पैरामीटर हैं, जो दिनांक सीमा और आईडी का प्रतिनिधित्व करते हैं।दूसरी ओर

with parameters as (
    select :start_date as start_date, 
      :end_date as end_date, 
      :search_id as search_id 
    from dual) 
select * 
from your_table 
    inner join parameters 
     on parameters.search_id = your_table.id; 

, यदि आप आईडी व समय अन्तराल पर खोज करने के लिए की जरूरत है, यह ऐसा दिखाई दे सकता: तुम सिर्फ आईडी पर खोज करना चाहते हैं, तो आप क्वेरी एक साथ इस तरह डाल सकता है

with parameters as (
    select :start_date as start_date, 
      :end_date as end_date, 
      :search_id as search_id 
    from dual) 
select * 
from your_table 
    inner join parameters 
     on parameters.search_id = your_table.id 
      and your_table.create_date between (parameters.start_date 
               and parameters.end_date); 

यह इस से निपटने का एक राउंड के बारे में जिस तरह से की तरह लग सकता है, लेकिन अंत परिणाम यह है कि कोई फर्क नहीं पड़ता कि कैसे आप अपने गतिशील एसक्यूएल जटिल हो जाता है, जब तक कि यह केवल उन तीन मानकों की जरूरत है, PL/SQL कॉल है हमेशा कुछ ऐसा होता है:

execute immediate v_SQL using v_start_date, v_end_date, v_search_id; 

मेरे अनुभव में एसक्यूएल निर्माण को और अधिक जटिल बनाना बेहतर है ताकि यह सुनिश्चित किया जा सके कि केवल एक पंक्ति है जहां इसे वास्तव में निष्पादित किया जाता है।

+0

यह समाधान रोब (टॉम क्यटे के लेख) से घिरे हुए एक से अधिक स्पष्ट दिखता है लेकिन ओरेकल 10.2.0.5 पर परीक्षण के बाद मैंने पाया कि ऑप्टिमाइज़र टॉम के अपमान के साथ बेहतर काम करता है (यानी: अधिक चुनिंदा इंडेक्स चुनता है)। – Ochoto

2

एनवीएल दृष्टिकोण आमतौर पर ठीक काम करेगा। अनुकूलक इस पैटर्न को पहचानता है और एक गतिशील योजना का निर्माण करेगा। योजना एक मूल्य के लिए एक सूचकांक का उपयोग करती है और एक पूर्ण के लिए एक पूर्ण तालिका स्कैन का उपयोग करती है।

नमूना तालिका और डेटा

drop table myTable; 
create table myTable(
    id_pk number, 
    value varchar2(100), 
    constraint myTable_pk primary key (id_pk) 
); 

insert into myTable select level, level from dual connect by level <= 100000; 
commit; 

अलग विधेय साथ निष्पादित

--Execute predicates that return one row if the ID is set, or all rows if ID is null. 
declare 
    type t_table is table of myTable%rowtype; 
    n_RequiredId myTable.id_pk%type := 1; 
    t_Output t_table := t_table(); 
begin 
    select /*+ SO_QUERY_1 */ m.id_pk, m.value 
    bulk collect into t_Output 
    from myTable m 
    where m.id_pk = nvl(n_RequiredId, m.id_pk); 

    select /*+ SO_QUERY_2 */ m.id_pk, m.value 
    bulk collect into t_Output 
    from myTable m 
    where m.id_pk = COALESCE(n_RequiredId, m.id_pk); 

    select /*+ SO_QUERY_3 */ m.id_pk, m.value 
    bulk collect into t_Output 
    from myTable m 
    where (n_RequiredId IS NULL OR m.id_pk = n_RequiredId); 
end; 
/

प्राप्त करें निष्पादन की योजना बना रही

select sql_id, child_number 
from gv$sql 
where lower(sql_text) like '%so_query_%' 
    and sql_text not like '%QUINE%' 
    and sql_text not like 'declare%'; 

select * from table(dbms_xplan.display_cursor(sql_id => '76ucq3bkgt0qa', cursor_child_no => 1, format => 'basic')); 
select * from table(dbms_xplan.display_cursor(sql_id => '4vxf8yy5xd6qv', cursor_child_no => 1, format => 'basic')); 
select * from table(dbms_xplan.display_cursor(sql_id => '457ypz0jpk3np', cursor_child_no => 1, format => 'basic')); 

COALESCE के लिए बुरा योजनाओं और शून्य है या

EXPLAINED SQL STATEMENT: 
------------------------ 
SELECT /*+ SO_QUERY_2 */ M.ID_PK, M.VALUE FROM MYTABLE M WHERE M.ID_PK 
= COALESCE(:B1 , M.ID_PK) 

Plan hash value: 1229213413 

------------------------------------- 
| Id | Operation   | Name | 
------------------------------------- 
| 0 | SELECT STATEMENT |   | 
| 1 | TABLE ACCESS FULL| MYTABLE | 
------------------------------------- 


EXPLAINED SQL STATEMENT: 
------------------------ 
SELECT /*+ SO_QUERY_3 */ M.ID_PK, M.VALUE FROM MYTABLE M WHERE (:B1 IS 
NULL OR M.ID_PK = :B1) 

Plan hash value: 1229213413 

------------------------------------- 
| Id | Operation   | Name | 
------------------------------------- 
| 0 | SELECT STATEMENT |   | 
| 1 | TABLE ACCESS FULL| MYTABLE | 
------------------------------------- 

अच्छा योजना NVL

FILTER के संचालन के लिए इनपुट मानों के आधार पर, अनुकूलक रन टाइम पर एक अलग योजना चयन करने की अनुमति।

EXPLAINED SQL STATEMENT: 
------------------------ 
SELECT /*+ SO_QUERY_1 */ M.ID_PK, M.VALUE FROM MYTABLE M WHERE M.ID_PK 
= NVL(:B1 , M.ID_PK) 

Plan hash value: 730481884 

---------------------------------------------------- 
| Id | Operation      | Name  | 
---------------------------------------------------- 
| 0 | SELECT STATEMENT    |   | 
| 1 | CONCATENATION    |   | 
| 2 | FILTER      |   | 
| 3 | TABLE ACCESS FULL   | MYTABLE | 
| 4 | FILTER      |   | 
| 5 | TABLE ACCESS BY INDEX ROWID| MYTABLE | 
| 6 |  INDEX UNIQUE SCAN   | MYTABLE_PK | 
---------------------------------------------------- 

चेतावनी

FILTER संचालन और इस NVL चाल अच्छी तरह से प्रलेखित नहीं हैं। मुझे यकीन नहीं है कि किस संस्करण ने इन सुविधाओं को पेश किया लेकिन यह 11 जी के साथ काम करता है। मुझे कुछ जटिल प्रश्नों के साथ सही ढंग से काम करने के लिए FILTER प्राप्त करने में समस्याएं आई हैं, लेकिन इन जैसे सरल प्रश्नों के लिए यह विश्वसनीय है।

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