UPDATE
सिंटैक्स आवश्यकता लक्ष्य स्तंभों को स्पष्ट रूप से नाम देने के लिए। इससे बचने के संभावित कारण:
- आपके पास कई कॉलम हैं और आप सिर्फ वाक्य रचना को छोटा करना चाहते हैं।
- आप नहीं जानते हैं अद्वितीय कॉलम को छोड़कर कॉलम नाम।
"All columns"
मतलब "लक्ष्य तालिका के सभी कॉलम" . है (या कम से कम "तालिका के प्रमुख स्तंभ" ) मिलान क्रम और मिलान डेटा प्रकार में। अन्यथा आपको वैसे भी लक्ष्य स्तंभ नामों की एक सूची प्रदान करनी होगी।
टेस्ट टेबल:
CREATE TABLE tbl (
id int PRIMARY KEY
, text text
, extra text
);
INSERT INTO tbl AS t
VALUES (1, 'foo')
, (2, 'bar');
<एच3>1. DELETE
और INSERT
इसके बजाय एकल क्वेरी में
id
. को छोड़कर किसी भी स्तंभ नाम को जाने बिना .
केवल "लक्ष्य तालिका के सभी स्तंभों" . के लिए कार्य करता है . जबकि सिंटैक्स एक प्रमुख सबसेट के लिए भी काम करता है, लक्ष्य तालिका में अतिरिक्त कॉलम DELETE
के साथ NULL पर रीसेट हो जाएंगे। और INSERT
।
यूपीएसईआरटी (INSERT ... ON CONFLICT ...
) समवर्ती लेखन भार के तहत समवर्ती/लॉकिंग मुद्दों से बचने के लिए आवश्यक है, और केवल इसलिए कि पोस्टग्रेज़ में अभी तक मौजूद पंक्तियों को लॉक करने का कोई सामान्य तरीका नहीं है (वैल्यू लॉकिंग )।
आपकी विशेष आवश्यकता केवल UPDATE
को प्रभावित करती है अंश। संभावित जटिलताएं वहां लागू नहीं होती जहां मौजूदा पंक्तियाँ प्रभावित होती हैं। वे ठीक से बंद हैं। कुछ और सरल करते हुए, आप अपने मामले को कम करके DELETE
. कर सकते हैं और INSERT
:
WITH data(id) AS ( -- Only 1st column gets explicit name!
VALUES
(1, 'foo_upd', 'a') -- changed
, (2, 'bar', 'b') -- unchanged
, (3, 'baz', 'c') -- new
)
, del AS (
DELETE FROM tbl AS t
USING data d
WHERE t.id = d.id
-- AND t <> d -- optional, to avoid empty updates
) -- only works for complete rows
INSERT INTO tbl AS t
TABLE data -- short for: SELECT * FROM data
ON CONFLICT (id) DO NOTHING
RETURNING t.id;
पोस्टग्रेज एमवीसीसी मॉडल में, एक UPDATE
काफी हद तक DELETE
. जैसा ही है और INSERT
वैसे भी (कुछ कोने के मामलों को छोड़कर, समवर्ती, हॉट अपडेट और लाइन से बाहर संग्रहीत बड़े कॉलम मान)। चूँकि आप वैसे भी सभी पंक्तियों को बदलना चाहते हैं, बस INSERT
. से पहले परस्पर विरोधी पंक्तियों को हटा दें . लेन-देन के प्रतिबद्ध होने तक हटाई गई पंक्तियाँ लॉक रहती हैं। INSERT
पहले गैर-मौजूदा कुंजी मानों के लिए परस्पर विरोधी पंक्तियां तभी मिल सकती हैं, जब उन्हें समवर्ती रूप से सम्मिलित करने के लिए एक समवर्ती लेनदेन होता है (DELETE
के बाद) , लेकिन INSERT
. से पहले )।
आप इस विशेष मामले में प्रभावित पंक्तियों के लिए अतिरिक्त स्तंभ मान खो देंगे। कोई अपवाद नहीं उठाया। लेकिन अगर प्रतिस्पर्धी प्रश्नों को समान प्राथमिकता दी जाती है, तो यह शायद ही कोई समस्या है:दूसरी क्वेरी कुछ . के लिए जीती गई पंक्तियाँ। इसके अलावा, यदि अन्य क्वेरी एक समान यूपीएसईआरटी है, तो इसका विकल्प इस लेनदेन के लिए प्रतीक्षा करना और फिर तुरंत अपडेट करना है। "जीतना" एक पाइरिक जीत हो सकती है।
"खाली अपडेट" के बारे में:
- मैं कैसे (या मैं कर सकता हूं) एकाधिक स्तंभों पर DISTINCT का चयन कैसे करूं?
नहीं, मेरी क्वेरी जीतनी चाहिए!
ठीक है, आपने इसके लिए कहा:
WITH data(id) AS ( -- Only 1st column gets explicit name!
VALUES -- rest gets default names "column2", etc.
(1, 'foo_upd', NULL) -- changed
, (2, 'bar', NULL) -- unchanged
, (3, 'baz', NULL) -- new
, (4, 'baz', NULL) -- new
)
, ups AS (
INSERT INTO tbl AS t
TABLE data -- short for: SELECT * FROM data
ON CONFLICT (id) DO UPDATE
SET id = t.id
WHERE false -- never executed, but locks the row!
RETURNING t.id
)
, del AS (
DELETE FROM tbl AS t
USING data d
LEFT JOIN ups u USING (id)
WHERE u.id IS NULL -- not inserted !
AND t.id = d.id
-- AND t <> d -- avoid empty updates - only for full rows
RETURNING t.id
)
, ins AS (
INSERT INTO tbl AS t
SELECT *
FROM data
JOIN del USING (id) -- conflict impossible!
RETURNING id
)
SELECT ARRAY(TABLE ups) AS inserted -- with UPSERT
, ARRAY(TABLE ins) AS updated -- with DELETE & INSERT;
कैसे?
- पहला सीटीई
data
सिर्फ डेटा प्रदान करता है। इसके बजाय एक टेबल हो सकता है। - दूसरा सीटीई
ups
:यूपीएसईआरटी। परस्पर विरोधीid
वाली पंक्तियाँ बदले नहीं गए हैं, बल्कि लॉक किए गए . भी हैं । - तीसरा सीटीई
del
परस्पर विरोधी पंक्तियों को हटाता है। वे बंद रहते हैं। - चौथा सीटीई
ins
पूरी पंक्तियाँ सम्मिलित करता है . केवल उसी लेन-देन की अनुमति है - अंतिम चयन केवल डेमो दिखाने के लिए है कि क्या हुआ।
खाली अद्यतन परीक्षण (पहले और बाद में) के साथ जाँच करने के लिए:
SELECT ctid, * FROM tbl; -- did the ctid change?
(टिप्पणी की गई) पंक्ति में किसी भी परिवर्तन की जांच करें AND t <> d
NULL मानों के साथ भी काम करता है क्योंकि हम मैनुअल के अनुसार दो टाइप की गई पंक्ति मानों की तुलना कर रहे हैं:
दो NULL फ़ील्ड मान समान माने जाते हैं, और एक NULL को गैर-NULL से बड़ा माना जाता है
<एच3>2. गतिशील एसक्यूएलयह मौजूदा मानों को बनाए रखते हुए प्रमुख स्तंभों के सबसेट के लिए भी काम करता है।
चाल यह है कि पोस्टग्रेज को सिस्टम कैटलॉग से गतिशील रूप से कॉलम नामों के साथ क्वेरी स्ट्रिंग का निर्माण करने दें, और फिर इसे निष्पादित करें।
कोड के लिए संबंधित उत्तर देखें:
-
plpgsql में ट्रिगर फ़ंक्शन में एकाधिक कॉलम अपडेट करें
-
सभी स्तंभों का बल्क अपडेट
-
SQL एक तालिका के फ़ील्ड को दूसरे के फ़ील्ड से अपडेट करें