2012-12-19 58 views
21

द्वारा ग्रुप बनाम समूह चुनें, मैं मौजूदा ओरेकल डेटाबेस-संचालित एप्लिकेशन के लिए क्वेरी समय सुधारने की कोशिश कर रहा हूं जो थोड़ा सुस्त चल रहा है। आवेदन कई बड़े प्रश्नों को निष्पादित करता है, जैसे नीचे दिया गया, जिसमें चलाने के लिए एक घंटे लग सकते हैं। DISTINCT को GROUP BY क्लॉज के साथ नीचे दी गई क्वेरी में 100 मिनट से 10 सेकंड तक निष्पादन समय को घटा दिया गया है। मेरी समझ यह थी कि SELECT DISTINCT और GROUP BY बहुत अधिक तरीके से संचालित होते थे। निष्पादन के समय के बीच इतनी बड़ी असमानता क्यों? बैक एंड पर क्वेरी को कैसे निष्पादित किया जाता है, इसमें अंतर क्या है? क्या ऐसी कोई स्थिति है जहां SELECT DISTINCT तेजी से चलता है?एसक्यूएल प्रदर्शन:

नोट: निम्न क्वेरी में, WHERE TASK_INVENTORY_STEP.STEP_TYPE = 'TYPE A' केवल उन तरीकों में से एक का प्रतिनिधित्व करता है जिनके परिणाम फ़िल्टर किए जा सकते हैं।

SELECT DISTINCT 
    ITEMS.ITEM_ID, 
    ITEMS.ITEM_CODE, 
    ITEMS.ITEMTYPE, 
    ITEM_TRANSACTIONS.STATUS, 
    (SELECT COUNT(PKID) 
     FROM ITEM_PARENTS 
     WHERE PARENT_ITEM_ID = ITEMS.ITEM_ID 
     ) AS CHILD_COUNT 
FROM 
    ITEMS 
    INNER JOIN ITEM_TRANSACTIONS 
     ON ITEMS.ITEM_ID = ITEM_TRANSACTIONS.ITEM_ID 
     AND ITEM_TRANSACTIONS.FLAG = 1 
    LEFT OUTER JOIN ITEM_METADATA 
     ON ITEMS.ITEM_ID = ITEM_METADATA.ITEM_ID 
    LEFT OUTER JOIN JOB_INVENTORY 
     ON ITEMS.ITEM_ID = JOB_INVENTORY.ITEM_ID  
    LEFT OUTER JOIN JOB_TASK_INVENTORY 
     ON JOB_INVENTORY.JOB_ITEM_ID = JOB_TASK_INVENTORY.JOB_ITEM_ID 
    LEFT OUTER JOIN JOB_TASKS 
     ON JOB_TASK_INVENTORY.TASKID = JOB_TASKS.TASKID        
    LEFT OUTER JOIN JOBS 
     ON JOB_TASKS.JOB_ID = JOBS.JOB_ID 
    LEFT OUTER JOIN TASK_INVENTORY_STEP 
     ON JOB_INVENTORY.JOB_ITEM_ID = TASK_INVENTORY_STEP.JOB_ITEM_ID 
    LEFT OUTER JOIN TASK_STEP_INFORMATION 
     ON TASK_INVENTORY_STEP.JOB_ITEM_ID = TASK_STEP_INFORMATION.JOB_ITEM_ID 
WHERE 
    TASK_INVENTORY_STEP.STEP_TYPE = 'TYPE A' 
ORDER BY 
    ITEMS.ITEM_CODE 

एसक्यूएल: इस उदाहरण

एसक्यूएल DISTINCT का उपयोग कर तालिकाओं SELECT में शामिल कॉलम नहीं है और सभी उपलब्ध डेटा का दसवां हिस्सा बारे में परिणाम होगा के सभी शामिल होने के लिए तर्क को दिखाने के लिए प्रदान किया गया GROUP BY का उपयोग कर:

SELECT 
    ITEMS.ITEM_ID, 
    ITEMS.ITEM_CODE, 
    ITEMS.ITEMTYPE, 
    ITEM_TRANSACTIONS.STATUS, 
    (SELECT COUNT(PKID) 
     FROM ITEM_PARENTS 
     WHERE PARENT_ITEM_ID = ITEMS.ITEM_ID 
     ) AS CHILD_COUNT 
FROM 
    ITEMS 
    INNER JOIN ITEM_TRANSACTIONS 
     ON ITEMS.ITEM_ID = ITEM_TRANSACTIONS.ITEM_ID 
     AND ITEM_TRANSACTIONS.FLAG = 1 
    LEFT OUTER JOIN ITEM_METADATA 
     ON ITEMS.ITEM_ID = ITEM_METADATA.ITEM_ID 
    LEFT OUTER JOIN JOB_INVENTORY 
     ON ITEMS.ITEM_ID = JOB_INVENTORY.ITEM_ID  
    LEFT OUTER JOIN JOB_TASK_INVENTORY 
     ON JOB_INVENTORY.JOB_ITEM_ID = JOB_TASK_INVENTORY.JOB_ITEM_ID 
    LEFT OUTER JOIN JOB_TASKS 
     ON JOB_TASK_INVENTORY.TASKID = JOB_TASKS.TASKID        
    LEFT OUTER JOIN JOBS 
     ON JOB_TASKS.JOB_ID = JOBS.JOB_ID 
    LEFT OUTER JOIN TASK_INVENTORY_STEP 
     ON JOB_INVENTORY.JOB_ITEM_ID = TASK_INVENTORY_STEP.JOB_ITEM_ID 
    LEFT OUTER JOIN TASK_STEP_INFORMATION 
     ON TASK_INVENTORY_STEP.JOB_ITEM_ID = TASK_STEP_INFORMATION.JOB_ITEM_ID 
WHERE 
    TASK_INVENTORY_STEP.STEP_TYPE = 'TYPE A' 
GROUP BY 
    ITEMS.ITEM_ID, 
    ITEMS.ITEM_CODE, 
    ITEMS.ITEMTYPE, 
    ITEM_TRANSACTIONS.STATUS 
ORDER BY 
    ITEMS.ITEM_CODE 

यहाँ DISTINCT का उपयोग कर क्वेरी के लिए ओरेकल क्वेरी योजना है:

Oracle query plan for query using DISTINCT

यहाँ GROUP BY का उपयोग कर क्वेरी के लिए ओरेकल क्वेरी योजना है:

Oracle query plan for query using GROUP BY

+2

'समूह द्वारा' के साथ क्वेरी दिखाएं। –

+0

मेरे पास आपके प्रश्न का उत्तर नहीं है, लेकिन मुझे उम्मीद है कि दोनों प्रश्नों को देखते हुए, उनकी व्याख्या योजनाएं और लॉजिकल जीईटी की संख्या समझने में मदद कर सकती है । – symcbean

+0

SQL सर्वर में आप क्वेरी निष्पादन योजनाएं प्राप्त कर सकते हैं .. क्या आप ओरेकल में कुछ समान प्राप्त कर सकते हैं? यह आपको बताएगा कि अंतर कहां था। –

उत्तर

16

प्रदर्शन अंतर शायद SELECT खंड में उपकुंजी के निष्पादन के कारण है। मुझे लगता है कि यह से पहले प्रत्येक पंक्ति के लिए इस क्वेरी को फिर से निष्पादित कर रहा है। group by के लिए, यह समूह पर समूह के बाद निष्पादित करेगा।

, एक में शामिल होने और इसकी जगह के बजाय का प्रयास करें:

select . . ., 
     parentcnt 
from . . . left outer join 
     (SELECT PARENT_ITEM_ID, COUNT(PKID) as parentcnt 
     FROM ITEM_PARENTS 
    ) p 
     on items.item_id = p.parent_item_id 
+0

+1 - यह वही है जो मैं भी सोच रहा था (संभावित समाधान सहित), लेकिन मुझे यकीन है कि ओरेकल के बारे में पर्याप्त जानकारी नहीं है। –

+1

यह बाधा प्रतीत होता है। मैंने ग्रुप बाय संस्करण (100 मिनट बनाम 20 सेकेंड) के रूप में जल्द से जल्द निष्पादित क्वेरी और निष्कासन को हटाने का प्रयास किया। धन्यवाद! कोड गंध के लिए – woemler

-3

आप ग्रुप BY का उपयोग प्रत्येक समूह के लिए कुल ऑपरेटरों लागू करते हैं और DISTINCT को चाहिए कि यदि आप केवल डुप्लिकेट निकालने की जरूरत है।

मुझे लगता है कि प्रदर्शन समान है।

आपके मामले में मुझे लगता है कि आपको ग्रुप बाय का उपयोग करना चाहिए।

16

मुझे पूरा यकीन है कि GROUP BY और DISTINCT लगभग समान निष्पादन योजना है।

अंतर यहाँ से हम अनुमान लगाना है (के बाद से हमारे पास नहीं है की योजना की व्याख्या) है कि इनलाइन सबक्वेरीGROUP BY लेकिन के बाद मार डाला जाता है DISTINCT पहले IMO है।

तो अगर आपकी क्वेरी 1M पंक्तियां वापस आती है और पंक्तियों 1K के लिए एकत्रित हो जाता है:

  • GROUP BY क्वेरी जबकि DISTINCT क्वेरी सबक्वेरी 1000000 बार चलाने है | सबक्वेरी 1000 बार,
  • चलाने होता।

टीकेप्रोफ व्याख्या योजना इस परिकल्पना को प्रदर्शित करने में मदद करेगी।


जब तक हम इस पर चर्चा कर रहे हैं, मुझे लगता है कि यह नोट करना रास्ता क्वेरी लिखा है पाठक के लिए और अनुकूलक करने के लिए दोनों को गुमराह है कि महत्वपूर्ण है: आप स्पष्ट रूप से आइटम/item_transactions से सभी पंक्तियों को खोजना चाहते हैं कि "टाइप ए" के मूल्य के साथ TASK_INVENTORY_STEP.STEP_TYPE है।

IMO आपकी क्वेरी के लिए एक बेहतर योजना के लिए होता है और अगर इस तरह लिखा और अधिक आसानी से पढ़े जा सकेंगे:

SELECT ITEMS.ITEM_ID, 
     ITEMS.ITEM_CODE, 
     ITEMS.ITEMTYPE, 
     ITEM_TRANSACTIONS.STATUS, 
     (SELECT COUNT(PKID) 
      FROM ITEM_PARENTS 
     WHERE PARENT_ITEM_ID = ITEMS.ITEM_ID) AS CHILD_COUNT 
    FROM ITEMS 
    JOIN ITEM_TRANSACTIONS 
    ON ITEMS.ITEM_ID = ITEM_TRANSACTIONS.ITEM_ID 
    AND ITEM_TRANSACTIONS.FLAG = 1 
WHERE EXISTS (SELECT NULL 
       FROM JOB_INVENTORY 
       JOIN TASK_INVENTORY_STEP 
        ON JOB_INVENTORY.JOB_ITEM_ID=TASK_INVENTORY_STEP.JOB_ITEM_ID 
       WHERE TASK_INVENTORY_STEP.STEP_TYPE = 'TYPE A' 
        AND ITEMS.ITEM_ID = JOB_INVENTORY.ITEM_ID) 

कई मामलों में, एक अलग एक संकेत है कि क्वेरी ठीक से नहीं लिखा गया है हो सकता है (क्योंकि एक अच्छी क्वेरी डुप्लिकेट वापस नहीं करनी चाहिए)।

ध्यान दें कि आपके मूल चयन में 4 टेबल का उपयोग नहीं किया जाता है।

+0

प्रतिक्रिया के लिए धन्यवाद। दिया गया प्रश्न केवल एक उदाहरण है जो परिणामों को फ़िल्टर करने के लिए उपयोग की जाने वाली दूरस्थ रूप से शामिल तालिकाओं में से एक दिखाता है। इस क्वेरी में शामिल लगभग हर तालिका के कॉलम संभावित रूप से WHERE खंड में उपयोग किए जा सकते हैं। – woemler

+0

आपको अभी भी DISTINCT के बजाय उचित होने पर सेमी-जॉइन (EXISTS या IN) का उपयोग करना चाहिए, यह भविष्य के पाठक और शायद अधिक अनुकूलक के लिए स्पष्ट है। –

8

पहली बात ध्यान दिया जाना चाहिए कि Distinct का प्रयोग होता है एक कोड गंध, उर्फ ​​विरोधी पैटर्न इंगित करता है। इसका आम तौर पर मतलब है कि एक लापता जुड़ाव या अतिरिक्त जुड़ाव है जो डुप्लिकेट डेटा उत्पन्न कर रहा है। उपर्युक्त आपकी क्वेरी को देखते हुए, मुझे लगता है कि group by क्यों तेज है (क्वेरी देखे बिना), यह है कि group by का स्थान लौटने वाले अंतराल की संख्या को कम कर देता है। जबकि distinct परिणाम सेट को निकाल रहा है और पंक्ति तुलनाओं से पंक्ति कर रहा है। दृष्टिकोण

अद्यतन

क्षमा करें, मैं और अधिक स्पष्ट किया जाना चाहिए था। रिकॉर्ड्स उत्पन्न होते हैं जब उपयोगकर्ता सिस्टम में कुछ कार्य करते हैं, इसलिए कोई शेड्यूल नहीं है। एक उपयोगकर्ता एक दिन या सैकड़ों प्रति घंटे में एक रिकॉर्ड उत्पन्न कर सकता है। महत्वपूर्ण बात यह है कि प्रत्येक बार कोई उपयोगकर्ता खोज करता है, अद्यतित रिकॉर्ड्स वापस लौटाया जाना चाहिए, जिससे मुझे संदेह हो जाता है कि एक भौतिक व्यू यहां काम करेगा, विशेष रूप से यदि क्वेरी पॉपुलटिंग में चलाने के लिए लंबा लगेगा ।

मेरा मानना ​​है कि यह एक materialized दृश्य का उपयोग करने के लिए सटीक कारण है। तो प्रक्रिया इस तरह से काम करेगी। आप लंबे समय तक चलने वाली क्वेरी को उस टुकड़े के रूप में लेते हैं जो आपके भौतिक दृश्य को बनाता है, क्योंकि हम जानते हैं कि उपयोगकर्ता सिस्टम में कुछ मनमाने ढंग से कार्य करने के बाद केवल "नया" डेटा की परवाह करता है। तो आप जो करना चाहते हैं वह इस आधार भौतिक दृश्य के खिलाफ पूछताछ है, जिसे बैक एंड पर लगातार ताज़ा किया जा सकता है, इसमें शामिल दृढ़ता रणनीति को भौतिक दृश्य को बाहर नहीं करना चाहिए (एक समय में कुछ सौ रिकॉर्ड जारी रखना किसी भी चीज को कुचल नहीं देगा)। यह ओरेकल को रीड लॉक पकड़ने की अनुमति देगा (ध्यान दें कि हमें परवाह नहीं है कि हमारे डेटा कितने स्रोत पढ़ते हैं, हम केवल लेखकों की परवाह करते हैं)। सबसे बुरे मामले में उपयोगकर्ता के पास माइक्रोसेकंड के लिए "पुराना" डेटा होगा, इसलिए जब तक कि यह वॉल स्ट्रीट पर एक वित्तीय व्यापार प्रणाली या परमाणु रिएक्टर के लिए एक प्रणाली नहीं है, तब तक इन "ब्लिप" को सबसे ईगल आंखों वाले उपयोगकर्ताओं द्वारा अनजान किया जाना चाहिए।ऐसा करने के तरीके के

कोड उदाहरण:

create materialized view dept_mv FOR UPDATE as select * from dept; 

अब इस के लिए महत्वपूर्ण जब तक आप 'डॉन के रूप में टी आह्वान ताज़ा आप मौजूदा डेटा के किसी भी खोना नहीं होगा। यह निर्धारित करने के लिए आप पर निर्भर रहेंगे कि आप अपने भौतिक दृश्य को फिर से "आधार रेखा" कब चाहते हैं (आधी रात शायद?)

+3

+1। पीके के माध्यम से तालिकाओं में शामिल होने वाली क्वेरीज डुप्लिकेट वापस नहीं करनी चाहिए; अगर वे शायद कुछ कर रहे हैं :) –

+0

आप इस बिंदु पर निश्चित रूप से सही हैं। स्कीमा बहुत खराब रूप से डिज़ाइन किया गया है, कई रिडंडेंसी के साथ, स्कीमा ओवरहाल के बिना नई टेबल के साथ मॉड्यूल रखने के कई सालों से। दुर्भाग्य से, मुझे जो कुछ है उसके साथ रहना है। – woemler