परमाणु लेनदेन में अद्वितीय उल्लंघन से बचें


15

क्या PostgreSQL में परमाणु लेनदेन संभव है?

विचार करें कि इन पंक्तियों के साथ मेरे पास तालिका श्रेणी है:

id|name
--|---------
1 |'tablets'
2 |'phones'

और स्तंभ नाम में अद्वितीय बाधा है।

अगर मैं कोशिश करूँ:

BEGIN;
update "category" set name = 'phones' where id = 1;
update "category" set name = 'tablets' where id = 2;
COMMIT;

मैं ला रहा हूँ:

ERROR:  duplicate key value violates unique constraint "category_name_key"
DETAIL:  Key (name)=(tablets) already exists.

जवाबों:


24

@Craig ने क्या प्रदान किया (और इसमें से कुछ को सही करने के अलावा):

प्रभावी Postgres 9.4 , UNIQUE, PRIMARY KEYऔर EXCLUDEबाधाओं तुरंत जाँच कर रहे हैं प्रत्येक पंक्ति के बाद जब परिभाषित NOT DEFERRABLE। यह अन्य प्रकार की NOT DEFERRABLEबाधाओं (वर्तमान में केवल REFERENCES(विदेशी कुंजी)) से भिन्न है, जो प्रत्येक कथन के बाद जाँची जाती हैं । एसओ पर इस संबंधित प्रश्न के तहत हमने यह सब काम किया:

यह एक (या या ) बाधा के लिए पर्याप्त नहीं है कि आप अपने स्टेटमेंट को कई स्टेटमेंट वर्क के साथ बना सकें।UNIQUEPRIMARY KEYEXCLUDEDEFERRABLE

और आप इस उद्देश्य के लिए उपयोग नहीं कर सकते ALTER TABLE ... ALTER CONSTRAINTप्रति प्रलेखन:

ALTER CONSTRAINT

यह प्रपत्र पहले बनाई गई बाधा की विशेषताओं को बदल देता है। वर्तमान में केवल विदेशी प्रमुख बाधाओं को बदला जा सकता है

बोल्ड जोर मेरा। इसके बजाय उपयोग करें:

ALTER TABLE t
   DROP CONSTRAINT category_name_key
 , ADD  CONSTRAINT category_name_key UNIQUE(name) DEFERRABLE;

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

एक के लिए एक बयान बाधा deferrable बनाने के लिए पर्याप्त है:

UPDATE category c
SET    name = c_old.name
FROM   category c_old
WHERE  c.id     IN (1,2)
AND    c_old.id IN (1,2)
AND    c.id <> c_old.id;

CTE के साथ एक प्रश्न भी एक ही कथन है:

WITH x AS (
    UPDATE category SET name = 'phones' WHERE id = 1
    )
UPDATE category SET name = 'tablets' WHERE id = 2;

हालाँकि , कई बयानों के साथ आपके कोड के लिए आपको (अतिरिक्त) वास्तव में बाधा को दूर करने की आवश्यकता है - या इसे परिभाषित करें क्योंकि INITIALLY DEFERREDया तो आमतौर पर ऊपर की तुलना में अधिक महंगा है। लेकिन यह आसानी से एक बयान में सब कुछ पैक करने के लिए संभव नहीं है।

BEGIN;
SET CONSTRAINTS category_name_key DEFERRED;
UPDATE category SET name = 'phones'  WHERE id = 1;
UPDATE category SET name = 'tablets' WHERE id = 2;
COMMIT;

हालांकि, बाधाओं के संबंध में एक सीमा से अवगत रहें FOREIGN KEYप्रति प्रलेखन:

संदर्भित तालिका संदर्भित तालिका में एक गैर-विलक्षण अद्वितीय या प्राथमिक कुंजी बाधा के कॉलम होने चाहिए ।

तो आप दोनों एक ही समय में नहीं हो सकते।


13

जैसा कि मैं इसे समझता हूं, यहां आपका मुद्दा यह है कि बाधा को प्रत्येक विवरण के बाद जांचा जाता है, लेकिन आप इसे लेन-देन के अंत में जांचना चाहते हैं, इसलिए यह मध्यवर्ती राज्यों की अनदेखी करने के बाद पहले वाले राज्य की तुलना करता है।

यदि हां, तो यह एक अड़चन बाधा से संभव है ।

दस्तावेज के रूप में देखें SET CONSTRAINTSऔर DEFERRABLEविवश करें CREATE TABLE

ध्यान दें कि स्थगित बाधाओं की लागत है - सिस्टम को प्रतिबद्ध समय पर जांच करने के लिए उनकी एक सूची रखनी होती है, इसलिए वे लेनदेन के लिए अच्छे नहीं होते हैं जो परिवर्तनों का एक बड़ा समूह बनाते हैं। वे जांच के लिए धीमे भी हैं।

तो मुझे लगता है कि आप शायद चाहते हैं:

ALTER TABLE mytable ALTER CONSTRAINT category_name_key DEFERRABLE;

ध्यान दें कि ALTER TABLEबाधाओं को स्थापित करने पर एक सीमा प्रतीत होती है DEFERRABLE; आपको इसके बजाय होना पड़ सकता हैDROP और ADDबाधा को फिर से ।

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