CTE में डाली गई पंक्तियों को एक ही स्टेटमेंट में अपडेट क्यों नहीं किया जा सकता है?


13

PostgreSQL 9.5 में, इसके साथ बनाई गई एक सरल तालिका दी गई है:

create table tbl (
    id serial primary key,
    val integer
);

मैं SQL को INSERT मान के लिए चलाता हूं, फिर उसी स्टेटमेंट में अपडेट करें:

WITH newval AS (
    INSERT INTO tbl(val) VALUES (1) RETURNING id
) UPDATE tbl SET val=2 FROM newval WHERE tbl.id=newval.id;

परिणाम यह है कि अद्यतन को अनदेखा किया जाता है:

testdb=> select * from tbl;
┌────┬─────┐
 id  val 
├────┼─────┤
  1    1 
└────┴─────┘

ऐसा क्यों है? क्या यह SQL मानक (अर्थात अन्य डेटाबेस में मौजूद), या PostgreSQL के लिए कुछ विशिष्ट है जो भविष्य में तय हो सकता है? साथ प्रश्नों प्रलेखन का कहना है कई अपडेट समर्थित नहीं हैं, लेकिन सम्मिलित करता है और अद्यतन के उल्लेख नहीं है।

जवाबों:


15

एक सीटीई में सभी बयान वस्तुतः एक ही समय में होते हैं। यानी, वे डेटाबेस के एक ही स्नैपशॉट पर आधारित हैं।

UPDATEके रूप में अंतर्निहित तालिका के एक ही राज्य में देखता है INSERTजिसके साथ पंक्ति का अर्थ है, val = 1वहाँ नहीं है, अभी तक। मैनुअल यहाँ स्पष्ट करता है:

सभी बयानों को एक ही स्नैपशॉट के साथ निष्पादित किया जाता है ( अध्याय 13 देखें ), इसलिए वे लक्ष्य तालिकाओं पर एक दूसरे के प्रभावों को "नहीं" देख सकते हैं।

प्रत्येक स्टेटमेंट यह देख सकता है कि RETURNINGक्लॉज़ में दूसरे CTE द्वारा क्या लौटाया गया है । लेकिन अंतर्निहित तालिकाओं को सभी समान दिखते हैं।

आप क्या करने की कोशिश कर रहे हैं, इसके लिए आपको दो बयान (एक ही लेनदेन में) की आवश्यकता होगी। दिए गए उदाहरण को वास्तव में INSERTशुरू करने के लिए केवल एक होना चाहिए , लेकिन यह सरल उदाहरण के कारण हो सकता है।


15

यह एक कार्यान्वयन निर्णय है। यह Postgres प्रलेखन, WITHक्वेरीज़ (सामान्य तालिका अभिव्यक्तियाँ) में वर्णित है । मुद्दे से संबंधित दो पैराग्राफ हैं।

सबसे पहले, मनाया व्यवहार का कारण:

उप-कथनोंWITH को एक दूसरे के साथ और मुख्य क्वेरी के साथ समवर्ती रूप से निष्पादित किया जाता है । इसलिए, डेटा-संशोधित करने वाले कथनों का उपयोग करते समय WITH, जिस क्रम में निर्दिष्ट अद्यतन वास्तव में होता है वह अप्रत्याशित है। सभी बयानों को एक ही स्नैपशॉट के साथ निष्पादित किया जाता है (अध्याय 13 देखें), इसलिए वे लक्ष्य तालिकाओं पर एक दूसरे के प्रभाव को "नहीं" देख सकते हैं। यह पंक्ति अद्यतनों के वास्तविक क्रम की अप्रत्याशितता के प्रभावों को कम करता है, और इसका अर्थ है कि RETURNINGडेटा विभिन्न WITHउप-कथनों और मुख्य क्वेरी के बीच परिवर्तनों को संप्रेषित करने का एकमात्र तरीका है । इसका एक उदाहरण यह है कि ...

मैंने pgsql-docs के साथ एक सुझाव पोस्ट करने के बाद , मार्को टिक्काजा ने समझाया (जो इरविन के जवाब से सहमत है):

इन्सर्ट-अपडेट और इंसर्ट-डिलीट केस काम नहीं करते क्योंकि UPDATEs और DELETEs के पास INSERTed पंक्तियों को देखने का कोई तरीका नहीं है क्योंकि उनके स्नैपशॉट को INSERT होने से पहले लिया गया है। इन दोनों मामलों के बारे में कुछ भी अप्रत्याशित नहीं है।

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

WITH newval AS (
    INSERT INTO tbl(val) VALUES (1) RETURNING id
) UPDATE tbl SET val=2 FROM newval WHERE tbl.id=newval.id;

हमारे पास 2 भाग हैं, CTE ( newval):

-- newval
     INSERT INTO tbl(val) VALUES (1) RETURNING id

और मुख्य प्रश्न:

-- main 
UPDATE tbl SET val=2 FROM newval WHERE tbl.id=newval.id

निष्पादन का प्रवाह कुछ इस प्रकार है:

           initial data: tbl
                id  val 
                 (empty)
               /         \
              /           \
             /             \
    newval:                 \
       tbl (after newval)    \
           id  val           \
            1    1           |
                              |
    newval: returns           |
           id                 |
            1                 |
               \              |
                \             |
                 \            |
                    main query

नतीजतन, जब मुख्य क्वेरी तालिका के tblसाथ (स्नैपशॉट में देखी गई) में newvalमिलती है, तो यह 1-पंक्ति तालिका के साथ एक खाली तालिका में मिलती है। जाहिर है कि यह 0 पंक्तियों को अद्यतन करता है। इसलिए बयान कभी भी नई सम्मिलित पंक्ति को संशोधित करने के लिए नहीं आया और यही आप देखते हैं।

आपके मामले में समाधान, या तो पहले से सही मान सम्मिलित करने के लिए कथन को फिर से लिखना है या 2 कथन का उपयोग करना है। एक वो जो इन्सर्ट करता है और दूसरा अपडेट करने के लिए।


इसी तरह की अन्य स्थितियां भी हैं, जैसे कि यदि कथन एक था INSERTऔर फिर DELETEउसी पंक्तियों पर। यह ठीक उसी कारणों से विफल हो जाएगा।

कुछ अन्य मामले, अपडेट-अपडेट और अपडेट-डिलीट के साथ और उनके व्यवहार को एक ही डॉक्स पृष्ठ में, निम्नलिखित पैराग्राफ में समझाया गया है।

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

और मार्को तिइकजा के जवाब में:

अद्यतन अद्यतन और अद्यतन हटाना रद्द करें मामलों स्पष्ट रूप से कर रहे हैं नहीं एक ही अंतर्निहित कार्यान्वयन विस्तार (डालने अद्यतन और डालने हटाना रद्द मामलों के रूप में) की वजह से।
अपडेट-अपडेट का मामला काम नहीं करता क्योंकि यह आंतरिक रूप से हैलोवीन समस्या की तरह दिखता है, और पोस्टग्रेज के पास यह जानने का कोई तरीका नहीं है कि कौन सी ट्यूपल्स को दो बार अपडेट करना ठीक होगा और कौन से लोग हैलोवीन समस्या को फिर से प्रस्तुत कर सकते हैं।

तो इसका कारण एक ही है (CTE को संशोधित कैसे किया जाता है और प्रत्येक CTE एक ही स्नैपशॉट को कैसे देखता है) लेकिन इन 2 मामलों में विवरण अलग-अलग हैं, क्योंकि वे अधिक जटिल हैं और परिणाम अपडेट-अपडेट के मामले में अप्रत्याशित हो सकते हैं।

सम्मिलित-अद्यतन (आपके मामले के रूप में) और एक समान प्रविष्टि-हटाएँ परिणाम अनुमानित हैं। केवल सम्मिलित होता है क्योंकि दूसरे ऑपरेशन (अपडेट या डिलीट) के पास नई सम्मिलित पंक्तियों को देखने और प्रभावित करने का कोई तरीका नहीं है।


सुझाए गए समाधान हालांकि सभी मामलों के लिए समान हैं जो एक ही पंक्तियों को एक से अधिक बार संशोधित करने का प्रयास करते हैं: ऐसा न करें। या तो ऐसे कथन लिखें जो प्रत्येक पंक्ति को एक बार संशोधित करते हैं या अलग (2 या अधिक) कथनों का उपयोग करते हैं।

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