ON CONFLICT DO UPDATE
व्यवहार को स्पष्ट करें
यहां मैनुअल पर विचार करें :
सम्मिलन के लिए प्रस्तावित प्रत्येक व्यक्तिगत पंक्ति के लिए, या तो सम्मिलन आगे बढ़ता है, या, यदि एक मध्यस्थ बाधा या सूचकांक द्वारा निर्दिष्ट conflict_target
का उल्लंघन किया
जाता है, तो विकल्प conflict_action
लिया जाता है।
बोल्ड जोर मेरा। इसलिए आपको WHERE
खंड UPDATE
( यू conflict_action
) में अद्वितीय सूचकांक में शामिल स्तंभों के लिए विधेय को दोहराने की आवश्यकता नहीं है :
INSERT INTO test_upsert AS tu
(name , status, test_field , identifier, count)
VALUES ('shaun', 1 , 'test value', 'ident' , 1)
ON CONFLICT (name, status, test_field) DO UPDATE
SET count = tu.count + 1;
WHERE tu.name = 'shaun' AND tu.status = 1 AND tu.test_field = 'test value'
अद्वितीय उल्लंघन पहले से ही स्थापित करता है कि आपका जोड़ा WHERE
क्लॉज क्या बेमानी तरीके से लागू होगा।
आंशिक सूचकांक को स्पष्ट करें
WHERE
इसे एक वास्तविक आंशिक अनुक्रमणिका बनाने के लिए एक खंड जोड़ें जैसे कि आपने स्वयं उल्लेख किया है (लेकिन उल्टे तर्क के साथ):
CREATE UNIQUE INDEX test_upsert_partial_idx
ON public.test_upsert (name, status)
WHERE test_field IS NULL; -- not: "is not null"
अपने UPSERT में इस आंशिक सूचकांक का उपयोग करने के लिए आपको @ypercube जैसे मिलान की आवश्यकता होती है :conflict_target
ON CONFLICT (name, status) WHERE test_field IS NULL
अब उपरोक्त आंशिक सूचकांक अनुमान है। हालाँकि , जैसा कि मैनुअल भी नोट करता है :
[...] एक गैर-आंशिक अनूठे सूचकांक (एक विधेय के बिना एक अद्वितीय सूचकांक) का अनुमान लगाया जाएगा (और इस प्रकार इसका उपयोग किया जाता है ON CONFLICT
) यदि ऐसा सूचकांक हर दूसरे मानदंडों को पूरा करता है।
यदि आपके पास केवल (name, status)
(यह भी) पर एक अतिरिक्त (या केवल) सूचकांक का उपयोग किया जाता है। एक सूचकांक (name, status, test_field)
स्पष्ट रूप से अनुमानित नहीं होगा। यह आपकी समस्या की व्याख्या नहीं करता है, लेकिन हो सकता है कि परीक्षण करते समय भ्रम की स्थिति में शामिल हो गया हो।
समाधान
AIUI, उपरोक्त में से कोई भी आपकी समस्या को हल नहीं करता है , फिर भी। आंशिक सूचकांक के साथ, केवल NULL मान मिलान वाले विशेष मामले पकड़े जाएंगे। और अन्य डुप्लिकेट पंक्तियाँ या तो डाली जाएँगी यदि आपके पास कोई अन्य विशिष्ट अनुक्रमणिका / बाधाएँ मेल नहीं खाती हैं, या यदि आप करते हैं तो एक अपवाद बढ़ाएँ। मुझे लगता है कि वह नहीं है जो आप चाहते हैं। तुम लिखो:
मिश्रित कुंजी 20 स्तंभों से बना है, जिनमें से 10 अशक्त हो सकते हैं।
आप वास्तव में डुप्लिकेट को क्या मानते हैं? Postgres (SQL मानक के अनुसार) दो NULL मानों को समान नहीं मानता है। नियम पुस्तिका:
सामान्य तौर पर, एक अद्वितीय बाधा का उल्लंघन किया जाता है यदि तालिका में एक से अधिक पंक्ति होती है जहां बाधा में शामिल सभी स्तंभों के मान समान होते हैं। हालांकि, इस तुलना में दो शून्य मान कभी भी समान नहीं माने जाते हैं। इसका मतलब है कि एक अद्वितीय बाधा की उपस्थिति में भी डुप्लिकेट पंक्तियों को संग्रहीत करना संभव है, जिसमें कम से कम एक बाधा वाले कॉलम में एक शून्य मान होता है। यह व्यवहार SQL मानक के अनुरूप है, लेकिन हमने सुना है कि अन्य SQL डेटाबेस इस नियम का पालन नहीं कर सकते हैं। इसलिए उन अनुप्रयोगों को विकसित करते समय सावधान रहें जो पोर्टेबल होने का इरादा रखते हैं।
सम्बंधित:
मुझे लगता है कि आपNULL
सभी 10 अशक्त स्तंभों में मूल्यों को समान समझनाचाहतेहैं। यह एक अतिरिक्त आंशिक सूचकांक के साथ एक एकल अशक्त स्तंभ को कवर करने के लिए सुरुचिपूर्ण और व्यावहारिक है जैसे यहां प्रदर्शित किया गया है:
लेकिन यह अधिक अशक्त स्तंभों के लिए जल्दी से हाथ से निकल जाता है। आपको अशक्त स्तंभों के हर अलग संयोजन के लिए एक आंशिक सूचकांक की आवश्यकता होगी। उनमें से सिर्फ 2 के लिए जो कि 3 आंशिक सूचकांक हैं (a)
, (b)
और (a,b)
। संख्या तेजी के साथ बढ़ रही है 2^n - 1
। आपके 10 अशक्त स्तंभों के लिए, NULL मानों के सभी संभावित संयोजनों को कवर करने के लिए, आपको पहले से ही 1023 आंशिक अनुक्रमित की आवश्यकता होगी। नही जाओ।
सरल समाधान: NULL मानों को बदलें और इसमें शामिल स्तंभों को परिभाषित करें NOT NULL
, और सब कुछ एक साधारण UNIQUE
बाधा के साथ ठीक काम करेगा ।
यदि यह विकल्प नहीं है, तो मैं सूचकांक COALESCE
में NULL को बदलने के लिए एक अभिव्यक्ति सूचकांक का सुझाव देता हूं :
CREATE UNIQUE INDEX test_upsert_solution_idx
ON test_upsert (name, status, COALESCE(test_field, ''));
खाली स्ट्रिंग ( ''
) वर्ण प्रकारों के लिए एक स्पष्ट उम्मीदवार है, लेकिन आप किसी भी कानूनी मूल्य का उपयोग कर सकते हैं जो या तो कभी नहीं दिखाई देता है या "अद्वितीय" की आपकी परिभाषा के अनुसार NULL के साथ जोड़ दिया जा सकता है ।
फिर इस कथन का उपयोग करें:
INSERT INTO test_upsert as tu(name,status,test_field,identifier, count)
VALUES ('shaun', 1, null , 'ident', 11) -- works with
, ('bob' , 2, 'test value', 'ident', 22) -- and without NULL
ON CONFLICT (name, status, COALESCE(test_field, '')) DO UPDATE -- match expr. index
SET count = COALESCE(tu.count + EXCLUDED.count, EXCLUDED.count, tu.count);
@Ypercube की तरह मुझे लगता है कि आप वास्तव count
में मौजूदा गिनती में जोड़ना चाहते हैं । चूंकि कॉलम NULL हो सकता है, NULL जोड़ने से कॉलम NULL सेट हो जाएगा। यदि आप परिभाषित करते हैं count NOT NULL
, तो आप सरल कर सकते हैं।
एक अन्य विचार के लिए सभी अद्वितीय उल्लंघनों को कवर करने के लिए बयान से बस विरोधाभासी_टैग को छोड़ना होगा । फिर आप "अनूठे" होने की अधिक परिष्कृत परिभाषा के लिए विभिन्न अद्वितीय अनुक्रमितों को परिभाषित कर सकते हैं। लेकिन इसके साथ उड़ान नहीं होगी । एक बार फिर मैनुअल:ON CONFLICT DO UPDATE
के लिए ON CONFLICT DO NOTHING
, यह एक विरोधाभास निर्दिष्ट करने के लिए वैकल्पिक है। जब छोड़ा जाता है, तो सभी प्रयोग करने योग्य बाधाओं (और अद्वितीय अनुक्रमित) के साथ संघर्ष को नियंत्रित किया जाता है। के लिए ON CONFLICT DO UPDATE
, एक विरोध_टार्ग प्रदान किया जाना चाहिए।
count = CASE WHEN EXCLUDED.count IS NULL THEN tu.count ELSE COALESCE(tu.count, 0) + COALESCE(EXCLUDED.count, 0) END
करने के लिए सरल किया जा सकताcount = COALESCE(tu.count+EXCLUDED.count, EXCLUDED.count, tu.count)