PostgreSQL में 9.1 या बाद के संस्करण आप इसे डेटा-संशोधित CTE . का उपयोग करके एकल कथन के साथ कर सकते हैं . यह आमतौर पर कम त्रुटि प्रवण होता है। यह छोटा करता है दो DELETEs के बीच की समय सीमा जिसमें एक दौड़ की स्थिति समवर्ती संचालन के साथ आश्चर्यजनक परिणाम हो सकते हैं:
WITH del_child AS (
DELETE FROM child
WHERE child_id = 1
RETURNING parent_id, child_id
)
DELETE FROM parent p
USING del_child x
WHERE p.parent_id = x.parent_id
AND NOT EXISTS (
SELECT 1
FROM child c
WHERE c.parent_id = x.parent_id
AND c.child_id <> x.child_id -- !
);
एसक्यूएल फिडल.
बच्चे को किसी भी मामले में हटा दिया जाता है। मैं मैनुअल उद्धृत करता हूं:
<ब्लॉकक्वॉट>
WITH
में डेटा-संशोधित विवरण ठीक एक बार निष्पादित होते हैं, औरहमेशा पूर्ण होने के लिए , इस बात से स्वतंत्र रूप से कि क्या प्राथमिक क्वेरी उनके आउटपुट के सभी (या वास्तव में कोई भी) पढ़ती है। ध्यान दें कि यह SELECT
. के नियम से अलग है में WITH
:जैसा कि पिछले अनुभाग में बताया गया है, SELECT
. का निष्पादन केवल तब तक ले जाया जाता है जब तक प्राथमिक क्वेरी इसके आउटपुट की मांग करती है।
पैरेंट को केवल तभी हटाया जाता है जब उसके पास कोई अन्य न हो बच्चे।
आखिरी शर्त पर ध्यान दें। किसी की अपेक्षा के विपरीत, यह आवश्यक है, क्योंकि:
उप-विवरण WITH
. में हैं समवर्ती रूप से निष्पादित किए जाते हैं एक दूसरे के साथ और मुख्य प्रश्न के साथ। इसलिए, WITH
में डेटा-संशोधित कथनों का उपयोग करते समय , जिस क्रम में निर्दिष्ट अद्यतन वास्तव में होते हैं वह अप्रत्याशित है। सभी कथनों को उसी स्नैपशॉट के साथ क्रियान्वित किया जाता है (अध्याय 13 देखें), इसलिए वे लक्ष्य तालिकाओं पर एक-दूसरे के प्रभावों को "देख" नहीं सकते हैं।
बोल्ड जोर मेरा।
मैंने कॉलम नाम का उपयोग किया parent_id
गैर-वर्णनात्मक id
. के स्थान पर ।
दौड़ की स्थिति को हटा दें
संभावित दौड़ स्थितियों को समाप्त करने के लिए जिनका मैंने ऊपर उल्लेख किया है पूरी तरह से , पैरेंट रो को लॉक करें पहले . बेशक, सभी इसी तरह के संचालन को इसे काम करने के लिए उसी प्रक्रिया का पालन करना चाहिए।
WITH lock_parent AS (
SELECT p.parent_id, c.child_id
FROM child c
JOIN parent p ON p.parent_id = c.parent_id
WHERE c.child_id = 12 -- provide child_id here once
FOR NO KEY UPDATE -- locks parent row.
)
, del_child AS (
DELETE FROM child c
USING lock_parent l
WHERE c.child_id = l.child_id
)
DELETE FROM parent p
USING lock_parent l
WHERE p.parent_id = l.parent_id
AND NOT EXISTS (
SELECT 1
FROM child c
WHERE c.parent_id = l.parent_id
AND c.child_id <> l.child_id -- !
);
इस तरह केवल एक एक ही समय में लेन-देन एक ही माता-पिता को लॉक कर सकता है। तो ऐसा नहीं हो सकता है कि कई लेन-देन एक ही माता-पिता के बच्चों को हटा दें, फिर भी अन्य बच्चों को देखें और माता-पिता को छोड़ दें, जबकि सभी बच्चे बाद में चले गए हैं। (गैर-कुंजी कॉलम पर अपडेट की अभी भी अनुमति है FOR NO KEY UPDATE
।)
यदि ऐसे मामले कभी नहीं होते हैं या आप इसके साथ (शायद ही कभी) रह सकते हैं - पहली क्वेरी सस्ती है। नहीं तो यह सुरक्षित रास्ता है।
FOR NO KEY UPDATE
पोस्टग्रेज 9.4 के साथ पेश किया गया था। मैनुअल में विवरण। पुराने संस्करणों में मजबूत लॉक का उपयोग करें FOR UPDATE
इसके बजाय।