जब CTE के अंदर से कॉल किया जाता है तो PostgreSQL फ़ंक्शन निष्पादित नहीं होता है


16

बस मेरे अवलोकन की पुष्टि करने और इस बारे में स्पष्टीकरण प्राप्त करने की उम्मीद है कि ऐसा क्यों हो रहा है।

मेरे पास एक फंक्शन है:

CREATE OR REPLACE FUNCTION "public"."__post_users_id_coin" ("coins" integer, "userid" integer) RETURNS TABLE (id integer) AS '
UPDATE
users
SET
coin = coin + coins
WHERE
userid = users.id
RETURNING
users.id' LANGUAGE "sql" COST 100 ROWS 1000
VOLATILE
RETURNS NULL ON NULL INPUT
SECURITY INVOKER

जब मैं इस फ़ंक्शन को CTE से कॉल करता हूं, तो यह SQL कमांड निष्पादित करता है, लेकिन उदाहरण के लिए फ़ंक्शन को ट्रिगर नहीं करता है :

WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))

SELECT
1 -- Select 1 but update not performed

दूसरी ओर, यदि मैं फ़ंक्शन को CTE से कॉल करता हूं और फिर CTE के परिणाम का चयन करता हूं (या सीधे CTE के बिना फ़ंक्शन को कॉल करता हूं) तो यह SQL कमांड को निष्पादित करता है और उदाहरण के लिए फ़ंक्शन को ट्रिगर करता है :

WITH test AS
(SELECT * FROM __post_users_id_coin(10, 1))

SELECT
*
FROM
test -- Select result and update performed

या

SELECT * FROM __post_users_id_coin(10,1)

चूंकि मैं वास्तव में फ़ंक्शन के परिणाम के बारे में परवाह नहीं करता हूं (बस इसे अपडेट करने की आवश्यकता है) क्या सीटीई के परिणाम का चयन किए बिना इसे काम करने का कोई तरीका है?

जवाबों:


12

यह अपेक्षित व्यवहार है। सीटीई भौतिक हैं लेकिन एक अपवाद है।

यदि CTE को पैरेंट क्वेरी में संदर्भित नहीं किया जाता है तो यह बिल्कुल भी भौतिक नहीं है। आप उदाहरण के लिए यह कोशिश कर सकते हैं और यह ठीक चलेगा:

WITH not_executed AS (SELECT 1/0),
     executed AS (SELECT 1)
SELECT * FROM executed ;

क्रेग रिंगर के ब्लॉग पोस्ट में एक टिप्पणी से कॉपी किया गया कोड:
PostgreSQL के सीटीई अनुकूलन बाड़ हैं


यह और इसी तरह के कई प्रश्नों की कोशिश करने से पहले, मैंने सोचा था कि अपवाद था: "जब एक सीटीई को मूल प्रश्न या किसी अन्य सीटीई में संदर्भित नहीं किया जाता है और खुद को किसी अन्य सीटीई का संदर्भ नहीं देता है"। इसलिए, यदि आप चाहते थे कि सीटीई को निष्पादित किया जाए, लेकिन परिणाम क्वेरी परिणाम में नहीं दिखाए जाते हैं, तो मैंने सोचा कि यह एक वैकल्पिक हल होगा (इसे अन्य सीटीई में संदर्भित करता है)।

लेकिन अफसोस, यह मेरी उम्मीद के मुताबिक काम नहीं करता है:

WITH test AS
    (SELECT * FROM __post_users_id_coin(10, 1)),
  execute_test AS 
    (TABLE test)
SELECT 1 ;     -- no, it doesn't do the update

और इसलिए, मेरा "अपवाद नियम" सही नहीं है। जब CTE को किसी अन्य CTE द्वारा संदर्भित किया जाता है और उनमें से कोई भी पैरेंट क्वेरी द्वारा संदर्भित नहीं किया जाता है, तो स्थिति अधिक जटिल है और मुझे यकीन नहीं है कि CTE के भौतिक होने पर क्या होता है। मैं दस्तावेज़ीकरण में ऐसे मामलों के लिए कोई संदर्भ नहीं ढूँढ सकता।


आपके द्वारा पहले से सुझाई गई बातों का उपयोग करने से बेहतर कोई उपाय नहीं है:

SELECT * FROM __post_users_id_coin(10, 1) ;

या:

WITH test AS
    (SELECT * FROM __post_users_id_coin(10, 1))
SELECT *
FROM test ;

यदि फ़ंक्शन कई पंक्तियों को अद्यतन करता है और आपको 1परिणाम में कई पंक्तियाँ (साथ ) मिलती हैं, तो आप एकल पंक्ति प्राप्त करने के लिए एकत्रित हो सकते हैं:

SELECT MAX(1) AS result FROM __post_users_id_coin(10, 1) ;

लेकिन मैं उस फ़ंक्शन के परिणामों को पसंद करना चाहूंगा SELECT *जो आपके उदाहरण के साथ, एक अपडेट लौटाता है , इसलिए जो कुछ भी इस क्वेरी को जानता है अगर अपडेट थे और तालिका में क्या परिवर्तन थे।



5

यह अपेक्षित, प्रलेखित व्यवहार है।

टॉम लेन इसे यहाँ बताते हैं।

यहाँ मैनुअल में प्रलेखित:

डाटा में सुधार करने वाले बयान में WITHठीक एक बार क्रियान्वित कर रहे हैं, और हमेशा पूरा करने , चाहे की स्वतंत्र रूप से प्राथमिक क्वेरी उनके उत्पादन के सभी (या वास्तव में किसी भी) पढ़ता है। ध्यान दें कि यह इसके लिए नियम से अलग SELECTहै WITH: जैसा कि पिछले अनुभाग में कहा गया है, का निष्पादन SELECTकेवल उसी रूप में किया जाता है जहां तक ​​प्राथमिक क्वेरी इसके आउटपुट की मांग करती है

बोल्ड जोर मेरा। "डेटा-संशोधन" हैं INSERT, UPDATEऔर DELETEक्वेरीज़। (जैसा विरोध किया गया SELECT।) एक बार फिर मैनुअल:

आप (डेटा में सुधार करने वाले बयानों का उपयोग कर सकते INSERT, UPDATEया DELETE) में WITH

उचित कार्य

CREATE OR REPLACE FUNCTION public.__post_users_id_coin (_coins integer, _userid integer)
  RETURNS TABLE (id integer) AS
$func$
UPDATE users u
SET    coin = u.coin + _coins  -- see below
WHERE  u.id = _userid
RETURNING u.id
$func$ LANGUAGE sql COST 100 ROWS 1000 STRICT;

मैंने डिफ़ॉल्ट (शोर) क्लॉस को गिरा दिया और STRICTइसका संक्षिप्त पर्यायवाची हैRETURNS NULL ON NULL INPUT

किसी तरह सुनिश्चित करें कि पैरामीटर नाम कॉलम नामों के साथ संघर्ष न करें। मैं आगे बढ़ा _, लेकिन यह सिर्फ मेरी व्यक्तिगत पसंद है।

अगर मैं सुझाव दे coinसकता NULLहूं:

SET    coin = CASE WHEN coin IS NULL THEN _coins ELSE coin + _coins END

तो users.id, प्राथमिक कुंजी है तो न तो RETURNS TABLEहै और न ही ROWs 1000कोई मतलब। केवल एक पंक्ति को अद्यतन / वापस किया जा सकता है। लेकिन यह सब मुख्य बिंदु के बगल में है।

उचित कॉल करें

यह RETURNINGआपके फ़ंक्शन से क्लॉज़ और रिटर्न मान का उपयोग करने का कोई मतलब नहीं है यदि आप किसी भी तरह से कॉल में दिए गए मानों को अनदेखा करने जा रहे हैं। यह भी कोई मतलब नहीं है कि SELECT * FROM ...अगर आप उन्हें वैसे भी अनदेखा करते हैं तो लौटी हुई पंक्तियों को विघटित करना।

बस एक अदिश स्थिरांक लौटें ( RETURNING 1), फ़ंक्शन को इस रूप में परिभाषित करें RETURNS int(या RETURNINGपूरी तरह से छोड़ दें और इसे बनाएं RETURNS void) और इसके साथ कॉल करेंSELECT my_function(...)

समाधान

जब से तुम ...

वास्तव में परिणाम के बारे में परवाह नहीं है

.. बस SELECTएक निरंतर रूप CTE। इसे तब तक निष्पादित किए जाने की गारंटी है जब तक इसे बाहरी SELECT(प्रत्यक्ष या अप्रत्यक्ष रूप से) में संदर्भित किया जाता है ।

WITH test AS (SELECT __post_users_id_coin(10, 1))
SELECT 1 FROM test;

यदि आपके पास वास्तव में एक सेट-रिटर्निंग फ़ंक्शन है और फिर भी आउटपुट के बारे में परवाह नहीं है:

WITH test AS (SELECT * FROM __post_users_id_coin(10, 1))
SELECT 1 FROM test LIMIT 1;

1 पंक्ति से अधिक वापस जाने की आवश्यकता नहीं है। फ़ंक्शन को अभी भी कहा जाता है।

अंत में, यह स्पष्ट नहीं है कि आपको शुरू करने के लिए सीटीई की आवश्यकता क्यों है। शायद सिर्फ अवधारणा का एक प्रमाण।

बारीकी से संबंधित:

SO पर संबंधित उत्तर:

और विचार करें:


बहुत बड़ा, बड़ा प्रशंसक और आपका जवाब इरविन भी है। मैं सीटीई का उपयोग कर रहा हूं क्योंकि मैं एक ही रैपिंग फ़ंक्शन INSERTके UPDATEभीतर एक पूर्व कर रहा हूं - कोई लेनदेन उपलब्ध नहीं है।
एंडी

अच्छा लगा। बस aq: है testमें WITH test AS (SELECT * FROM __post_users_id_coin(10, 1)) SELECT ... LIMIT 1;एक संशोधित CTE या नहीं माना जाता?
ypercube y

@ ypercube y: SELECTCTE शब्दावली के अनुसार A "डेटा-संशोधित" नहीं है। मैंने ऊपर कुछ स्पष्टीकरण जोड़ा। यह उपयोगकर्ता की जिम्मेदारी है कि अगर वह पर्दे के पीछे डेटा को संशोधित करने वाले फ़ंक्शन में कोड जोड़ता है।
इरविन ब्रान्डेसटेटर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.