चयन और कहाँ खंड में एक ही समारोह


11

शुरुआती सवाल:

f(x, y)मेरे डेटाबेस टेबल में दो कॉलम x और y पर एक महंगा फ़ंक्शन है ।

मैं एक क्वेरी को निष्पादित करना चाहता हूं जो मुझे एक कॉलम के रूप में फ़ंक्शन का परिणाम देती है और इस पर एक बाधा डालती है, कुछ ऐसा

SELECT *, f(x, y) AS func FROM table_name WHERE func < 10;

हालाँकि यह काम नहीं करता है, इसलिए मुझे कुछ लिखना होगा

SELECT *, f(x, y) AS func FROM table_name WHERE f(x, y) < 10;

क्या यह दो बार महंगा फ़ंक्शन चलाएगा? ऐसा करने का सबसे अच्छा तरीका क्या है?


1
समारोह है STABLE/ IMMUTABLEया VOLATILE?
इवान कैरोल

जवाबों:


22

आइए एक फ़ंक्शन बनाएं जिसका साइड इफेक्ट हो ताकि हम यह देख सकें कि इसे कितनी बार निष्पादित किया गया है:

CREATE OR REPLACE FUNCTION test.this_here(val integer)
    RETURNS numeric
    LANGUAGE plpgsql
AS $function$
BEGIN
    RAISE WARNING 'I am called with %', val;
    RETURN sqrt(val);
END;
$function$;

और फिर इस तरह कॉल करें जैसे आप करते हैं:

SELECT this_here(i) FROM generate_series(1,10) AS t(i) WHERE this_here(i) < 2;

WARNING:  I am called with 1
WARNING:  I am called with 1
WARNING:  I am called with 2
WARNING:  I am called with 2
WARNING:  I am called with 3
WARNING:  I am called with 3
WARNING:  I am called with 4
WARNING:  I am called with 5
WARNING:  I am called with 6
WARNING:  I am called with 7
WARNING:  I am called with 8
WARNING:  I am called with 9
WARNING:  I am called with 10
    this_here     
──────────────────
                1
  1.4142135623731
 1.73205080756888
(3 rows)

जैसा कि आप देखते हैं, फ़ंक्शन को कम से कम एक बार ( WHEREखंड से) कहा जाता है , और जब स्थिति सच होती है, तो एक बार फिर से उत्पादन करने के लिए।

दूसरे निष्पादन से बचने के लिए, आप वही कर सकते हैं जो एडगर सुझाता है - अर्थात् क्वेरी को लपेटें और परिणाम सेट को फ़िल्टर करें:

SELECT * 
  FROM (SELECT this_here(i) AS val FROM generate_series(1,10) AS t(i)) x 
 WHERE x.val < 2;

WARNING:  I am called with 1
... every value only once ...
WARNING:  I am called with 10

यह जांचने के लिए कि यह कैसे काम करता है, कोई भी जा सकता है pg_stat_user_functionsऔर callsवहां जांच कर सकता है (दिया गया track_functionsहै, सभी के लिए सेट है)।

आइए ऐसी किसी चीज़ के साथ कोशिश करें जिसका कोई साइड इफ़ेक्ट न हो:

CREATE OR REPLACE FUNCTION test.simple(val numeric)
 RETURNS numeric
 LANGUAGE sql
AS $function$
SELECT sqrt(val);
$function$;

SELECT simple(i) AS v 
  FROM generate_series(1,10) AS t(i)
 WHERE simple(i) < 2;
-- output omitted

SELECT * FROM pg_stat_user_functions WHERE funcname = 'simple';
-- 0 rows

simple()वास्तव में बहुत सरल है , इसलिए इसे इनलेट किया जा सकता है , इसलिए यह दृश्य में दिखाई नहीं देता है। आइए इसे अप्राप्य बनाएं:

CREATE OR REPLACE FUNCTION test.other_one(val numeric)
 RETURNS numeric
 LANGUAGE sql
AS $function$
SELECT 1; -- to prevent inlining
SELECT sqrt(val);
$function$;

SELECT other_one(i) AS v
  FROM generate_series(1,10) AS t(i)
 WHERE other_one(i) < 2;

SELECT * FROM pg_stat_user_functions ;
 funcid  schemaname  funcname   calls  total_time  self_time 
────────┼────────────┼───────────┼───────┼────────────┼───────────
 124311  test        other_one     13       0.218      0.218

SELECT *
  FROM (SELECT other_one(i) AS v FROM generate_series(1,10) AS t(i)) x 
 WHERE v < 2;

SELECT * FROM pg_stat_user_functions ;
 funcid  schemaname  funcname   calls  total_time  self_time 
────────┼────────────┼───────────┼───────┼────────────┼───────────
 124311  test        other_one     23       0.293      0.293

जैसा कि दिखता है, चित्र साइड इफेक्ट्स के साथ या बिना समान है।

बदलने other_one()के लिए IMMUTABLE, परिवर्तन व्यवहार (शायद आश्चर्य की बात) बदतर करने के लिए, क्योंकि यह दोनों प्रश्नों में 13 बार बुलाया जाएगा।


क्या फ़ंक्शन को कॉल करने का निर्णय फिर से फ़ंक्शन के शरीर में साइड-इफेक्टिंग निर्देश की उपस्थिति से निर्धारित किया जा सकता है? क्या यह पता लगाना संभव है कि क्वेरी पैरामीटर (यदि, उदाहरण के लिए, इसका कोई साइड-इफ़ेक्टिंग भाग नहीं था) को देखते हुए एक ही पैरामीटर (एस) के साथ एक फ़ंक्शन को प्रति पंक्ति एक या कई बार कहा जाता है?
एंड्री एम

@AndriyM मैं हां की कल्पना कर सकता हूं, लेकिन वर्तमान में डिबगर के साथ खेलने के लिए समय नहीं है कि वास्तव में क्या कहा जाए। इनलेटेड फ़ंक्शंस के बारे में थोड़ा सा जोड़ देगा (जो ओपी को उम्मीद नहीं करनी चाहिए, जैसा कि यह लगता है)।
dezso

1
@AndriyM, के अनुसार: postgresql.org/docs/9.1/static/sql-createfunction.html यदि किसी फ़ंक्शन को IMMUTABLE या STABLE घोषित नहीं किया गया है तो VOLATILE माना जाता है। VOLATILE इंगित करता है कि फ़ंक्शन मान एकल तालिका स्कैन के भीतर भी बदल सकता है, इसलिए कोई अनुकूलन नहीं किया जा सकता है।
लेनार्ट

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.