आप इसे एक सबक्वायरी में लपेट सकते हैं लेकिन यह OFFSET 0
के बिना सुरक्षित होने की गारंटी नहीं है हैक। 9.3 में, LATERAL
. का उपयोग करें . समस्या पार्सर द्वारा प्रभावी रूप से मैक्रो-विस्तार *
. के कारण होती है एक कॉलम सूची में।
समाधान
कहां:
SELECT (my_func(x)).* FROM some_table;
my_func
evaluate का मूल्यांकन करेगा n
n
. के लिए समय फ़ंक्शन से परिणाम कॉलम, यह सूत्रीकरण:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table
) sub;
आम तौर पर नहीं होगा, और रनटाइम पर एक अतिरिक्त स्कैन जोड़ने के लिए नहीं जाता है। यह सुनिश्चित करने के लिए कि एकाधिक मूल्यांकन नहीं किया जाएगा, आप OFFSET 0
. का उपयोग कर सकते हैं CTE की सीमाओं को ऑप्टिमाइज़ करने में PostgreSQL की विफलता को हैक या दुरुपयोग करें:
SELECT (mf).* FROM (
SELECT my_func(x) AS mf FROM some_table OFFSET 0
) sub;
या:
WITH tmp(mf) AS (
SELECT my_func(x) FROM some_table
)
SELECT (mf).* FROM tmp;
PostgreSQL 9.3 में आप LATERAL
. का उपयोग कर सकते हैं एक स्वस्थ व्यवहार पाने के लिए:
SELECT mf.*
FROM some_table
LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;
LEFT JOIN LATERAL ... ON true
मूल क्वेरी की तरह सभी पंक्तियों को बरकरार रखता है, भले ही फ़ंक्शन कॉल कोई पंक्ति न लौटाए।
डेमो
एक ऐसा फ़ंक्शन बनाएं जो प्रदर्शन के रूप में इनलाइन करने योग्य न हो:
CREATE OR REPLACE FUNCTION my_func(integer)
RETURNS TABLE(a integer, b integer, c integer) AS $$
BEGIN
RAISE NOTICE 'my_func(%)',$1;
RETURN QUERY SELECT $1, $1, $1;
END;
$$ LANGUAGE plpgsql;
और डमी डेटा की एक तालिका:
CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;
फिर उपरोक्त संस्करणों का प्रयास करें। आप देखेंगे कि पहला प्रति आमंत्रण तीन नोटिस उठाता है; बाद वाला केवल एक ही उठाता है।
क्यों?
अच्छा प्रश्न। यह भयानक है।
ऐसा लगता है:
(func(x)).*
के रूप में विस्तारित किया गया है:
(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l
पार्सिंग में, debug_print_parse
. पर एक नज़र के अनुसार , debug_print_rewritten
और debug_print_plan
. (छंटनी) पार्स ट्री इस तरह दिखता है:
:targetList (
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 1
:resulttype 23
:resulttypmod -1
:resultcollid 0
}
:resno 1
:resname i
...
}
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 2
:resulttype 20
:resulttypmod -1
:resultcollid 0
}
:resno 2
:resname j
...
}
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 3
:...
}
:resno 3
:resname k
...
}
{TARGETENTRY
:expr
{FIELDSELECT
:arg
{FUNCEXPR
:funcid 57168
...
}
:fieldnum 4
...
}
:resno 4
:resname l
...
}
)
इसलिए मूल रूप से, हम नोड्स को क्लोन करके वाइल्डकार्ड का विस्तार करने के लिए एक डंब पार्सर हैक का उपयोग कर रहे हैं।