2015-05-15 1 views
7

क्या ओरेकल फ़ंक्शन के परिणाम को पुन: उपयोग करने के लिए संभव है जब इसे फ़ंक्शन परिणाम कैश के उपयोग के बिना एक ही क्वेरी (लेनदेन?) में कहा जाता है?ओरेकल प्रदर्शन: एकाधिक समान फ़ंक्शन कॉल निष्पादित करने वाली क्वेरी

जिस एप्लिकेशन के साथ मैं काम कर रहा हूं वह ओरेकल कार्यों पर काफी निर्भर है। कई प्रश्न एक ही सटीक कार्यों को कई बार निष्पादित करते हैं।

एक विशिष्ट उदाहरण होगा:

SELECT my_package.my_function(my_id), 
     my_package.my_function(my_id)/24, 
     my_package.function_also_calling_my_function(my_id) 
    FROM my_table 
WHERE my_table.id = my_id; 

मैं देखा है कि ओरेकल हमेशा इन कार्यों में से प्रत्येक को निष्पादित करता है, नहीं एहसास है कि एक ही समारोह में एक ही प्रश्न में सिर्फ एक सेकंड पहले बुलाया गया था। यह संभव है कि फ़ंक्शन में कुछ तत्व कैश हो जाएं, जिसके परिणामस्वरूप थोड़ा तेज वापसी हो जाएगी। यह मेरे प्रश्न के लिए प्रासंगिक नहीं है क्योंकि मैं पूरे दूसरे या तीसरे निष्पादन से बचना चाहता हूं।

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

हालांकि डेटा अक्सर बदल रहा है, मुझे उम्मीद है कि इन कार्यों के परिणाम एक ही प्रश्न से बुलाए जाने पर ही वही होंगे।

क्या ओरेकल के लिए इन कार्यों के परिणाम का पुन: उपयोग करना संभव है और कैसे? मैं Oracle11g और Oracle12c का उपयोग कर रहा हूँ।

-- Takes 200 ms 
SELECT test_package.testSpeed('STANDARD', 'REGEXP_COUNT') 
    FROM dual; 

-- Takes 400ms 
SELECT test_package.testSpeed('STANDARD', 'REGEXP_COUNT') 
    , test_package.testSpeed('STANDARD', 'REGEXP_COUNT') 
    FROM dual; 

प्रयुक्त कार्य::

CREATE OR REPLACE PACKAGE test_package IS 

FUNCTION testSpeed (p_package_name VARCHAR2, p_object_name VARCHAR2) 
RETURN NUMBER; 
END; 
/

CREATE OR REPLACE PACKAGE BODY test_package IS 

FUNCTION testSpeed (p_package_name VARCHAR2, p_object_name VARCHAR2) 
RETURN NUMBER 
IS 

    ln_total NUMBER; 

BEGIN 

    SELECT SUM(position) INTO ln_total 
     FROM all_arguments 
    WHERE package_name = 'STANDARD' 
     AND object_name = 'REGEXP_COUNT'; 

    RETURN ln_total; 

END testSpeed; 

END; 
/
+0

तुम अब भी तालिका परिवर्तनों को संभाल परिणाम कैशिंग और "relies_on" कीवर्ड का उपयोग कर सकते हैं - http://www.oracle.com/technetwork/issue-archive/2010/10-sep/o57plsql-088600.html एक बार क्लॉज का उपयोग कर फ़ंक्शन को कॉल क्यों न करें, फिर संदर्भ में आपकी क्वेरी का संदर्भ लें? – OldProgrammer

+0

12 सी में आप एसक्यूएल स्टेटमेंट के अंदर फ़ंक्शन घोषित कर सकते हैं जो कि पीएल/एसक्यूएल (लेनदेन संबंधी परिप्रेक्ष्य से) से बहुत अलग है – ibre5041

+0

एक खंड का उपयोग करने से यह मदद नहीं करता है क्योंकि यह दुर्भाग्य से दो बार फ़ंक्शन निष्पादित करता है। मैं नहीं देखता कि RELIES_ON कीवर्ड कुछ भी कैसे बदलता है, 11 जीआर 2 निर्भरताओं में स्वचालित रूप से पहचाना जाता है और तालिका को डेटा को कई बार बदलता है, कैश को अमान्य कर देता है। मैं विशेष रूप से एक ही प्रश्न के भीतर कैशिंग के लिए देख रहा हूँ। समारोह खंड के साथ 12 सी दिलचस्प है और मैं इसे यहां और वहां लागू करने में सक्षम हो सकता हूं, यह बड़ी समस्या का समाधान नहीं करता है, हालांकि जब नेस्टेड फ़ंक्शंस वाले फ़ंक्शन वास्तव में जटिल होते हैं और मैं जिस अनुप्रयोग ढांचे में काम कर रहा हूं उसका लाभ उठाता हूं। – jmuntingh

उत्तर

4

ओरेकल को एक क्वेरी क्वेरी में फिर से लिखने और कई बार कार्यों को निष्पादित करने से रोकने के लिए एक इनलाइन व्यू और ROWNUM जोड़ें।


नमूना समारोह और समस्या का प्रदर्शन

create or replace function wait_1_second return number is 
begin 
    execute immediate 'begin dbms_lock.sleep(1); end;'; 
    -- ... 
    -- Do something here to make caching impossible. 
    -- ... 
    return 1; 
end; 
/

--1 second 
select wait_1_second() from dual; 

--2 seconds 
select wait_1_second(), wait_1_second() from dual; 

--3 seconds 
select wait_1_second(), wait_1_second() , wait_1_second() from dual; 

सरल क्वेरी परिवर्तन जो काम नहीं करते

इन तरीकों में से दोनों अभी भी 2 सेकंड, नहीं 1.

ले
select x, x 
from 
(
    select wait_1_second() x from dual 
); 

with execute_function as (select wait_1_second() x from dual) 
select x, x from execute_function; 

ओरेकल जबरदस्ती एक विशेष क्रम

यह ओरेकल बताने के लिए मुश्किल है में निष्पादित करने के लिए "अपने आप में इस कोड को निष्पादित, किसी भी विधेय, धक्का विलय, या उस पर अन्य परिवर्तनों ऐसा नहीं करते हैं।" उनमें से प्रत्येक अनुकूलन के लिए संकेत हैं, लेकिन उनका उपयोग करना मुश्किल है। उन परिवर्तनों को अक्षम करने के कुछ तरीके हैं, अतिरिक्त ROWNUM जोड़ना आमतौर पर सबसे आसान है।

--Only takes 1 second 
select x, x 
from 
(
    select wait_1_second() x, rownum 
    from dual 
); 

यह देखना मुश्किल है कि कार्यों का मूल्यांकन कहां किया जाता है। लेकिन ये व्याख्या योजनाएं दिखाती हैं कि ROWNUM इनलाइन दृश्य को अलग से चलाने का कारण बनता है।

explain plan for select x, x from (select wait_1_second() x from dual); 
select * from table(dbms_xplan.display(format=>'basic')); 

Plan hash value: 1388734953 

--------------------------------- 
| Id | Operation  | Name | 
--------------------------------- 
| 0 | SELECT STATEMENT |  | 
| 1 | FAST DUAL  |  | 
--------------------------------- 

explain plan for select x, x from (select wait_1_second() x, rownum from dual); 
select * from table(dbms_xplan.display(format=>'basic')); 

Plan hash value: 1143117158 

--------------------------------- 
| Id | Operation  | Name | 
--------------------------------- 
| 0 | SELECT STATEMENT |  | 
| 1 | VIEW   |  | 
| 2 | COUNT   |  | 
| 3 | FAST DUAL  |  | 
--------------------------------- 
+0

वाह, यह बहुत अप्रत्याशित है।और उदाहरण के लिए, यह काम करता है! यह मेरी "बड़ी तस्वीर" समस्या के साथ अभी तक मदद नहीं करता है लेकिन मैं निश्चित रूप से कुछ मामलों में इसका उपयोग कर सकता हूं। एक बात हालांकि, मुझे समझ में नहीं आ रहा है - सब कुछ- क्यों ROWNUM जोड़ना व्याख्या योजना बदलता है/ओरेकल केवल एक बार समारोह निष्पादित करता है। क्या इसके लिए गहन स्पष्टीकरण है? मदद बहुत सराहना की है, धन्यवाद! – jmuntingh

+0

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

+0

शानदार, मैंने कभी इस बारे में सोचा नहीं होगा। मैं इसे सबसे अच्छे जवाब के रूप में चिह्नित करने जा रहा हूं क्योंकि मुझे नहीं लगता कि एक ही लेनदेन में "कैशिंग" का समाधान है। फ़ंक्शन परिणाम कैश शायद इसके लिए सबसे नज़दीकी चीज है। धन्यवाद! – jmuntingh

1

आप शुद्ध रूप में भी कार्य को चिह्नित करने के deterministic कीवर्ड की कोशिश कर सकते

नीचे (समस्या को वर्णन करने के लिए सिर्फ एक यादृच्छिक गैर भावना समारोह) एक उदाहरण है । यह वास्तव में प्रदर्शन में सुधार करता है या नहीं, हालांकि एक और सवाल है।

अद्यतन:

मैं नहीं जानता कि कैसे यथार्थवादी अपने उदाहरण से ऊपर है, लेकिन सिद्धांत रूप में आप हमेशा तो यह दोहराया कार्यों कॉल (वास्तव में दोहराया मान) के बारे में जानता संरचना फिर से करने के लिए अपने एसक्यूएल कोशिश कर सकते हैं।

select x,x from (
    SELECT test_package.testSpeed('STANDARD', 'REGEXP_COUNT') x 
     FROM dual 
) 
+3

के लिए काम कर सकती है, नहीं, यह एक निर्धारक कार्य नहीं है। यह हर बार एक ही इनपुट के लिए एक ही परिणाम वापस नहीं कर रहा है। – OldProgrammer

+0

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

+0

फ़ंक्शन निर्धारक नहीं हो सकता है क्योंकि निर्धारक की परिभाषा एक ही पैरामीटर के लिए एक ही परिणाम प्राप्त कर रही है। हालांकि इस मामले में, पैरामीटर समान हो सकते हैं लेकिन अंतर्निहित डेटा बदल सकता है, जिसके परिणामस्वरूप एक अलग वापसी मूल्य होता है। इसके अलावा, निर्धारिती जोड़ते समय भी, इससे कोई फर्क नहीं पड़ता है। यह अभी भी इसे दो बार निष्पादित करता है। – jmuntingh

0

एक इन-लाइन व्यू का उपयोग करें।

with get_functions as(
SELECT my_package.my_function(my_id) as func_val, 
    my_package.function_also_calling_my_function(my_id) func_val_2 
    FROM my_table 
WHERE my_table.id = my_id 
) 
select func_val, 
    func_val/24 as func_val_adj, 
    func_val_2 
from get_functions; 

यदि आप आइटम 3 के लिए कॉल को खत्म करना चाहते हैं, तो इसके बजाय func_val का परिणाम तीसरे फ़ंक्शन पर पास करें।

+0

मुझे यह नहीं मिला है, यह कोई चीज़ नहीं बदलेगा ... यह अभी भी सभी कार्यों को स्वतंत्र रूप से निष्पादित करता है। फ़िर भी सहायता के लिए धन्यवाद! संपादित करें: मैं समझता हूं कि अब आपका क्या मतलब है, यह वही नहीं है जो मैं बाद में हूं। मेरी गलती मेरे उदाहरण के रूप में समस्या के लिए एक बहुत ही सरल व्याख्या है। मैं देखूंगा कि क्या मैं बाद में अपनी समस्या के लिए एक और यथार्थवादी उदाहरण/स्पष्टीकरण जोड़ सकता हूं। – jmuntingh

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