पोस्टग्रेज में डिफ्रैबल यूनिक इंडेक्स


14

चेंजिंग टेबल के लिए पोस्टग्रेज डॉक्यूमेंटेशन को देखते हुए , ऐसा लगता है कि नियमित रूप से बाधाएं चिह्नित की जा सकती हैं DEFERRABLE(अधिक संक्षेप में INITIALLY DEFERRED, जो कि मुझे इसमें दिलचस्पी है)।

सूचकांक को एक बाधा के साथ भी जोड़ा जा सकता है, जब तक कि:

सूचकांक में न तो अभिव्यक्ति कॉलम हो सकता है और न ही आंशिक सूचकांक हो सकता है

जो मुझे विश्वास दिलाता है कि वर्तमान में स्थितियों के साथ एक अद्वितीय सूचकांक रखने का कोई तरीका नहीं है, जैसे:

CREATE UNIQUE INDEX unique_booking
  ON public.booking
  USING btree
  (check_in, check_out)
  WHERE booking_status = 1;

होने का INITIALLY DEFERREDअर्थ है, कि विशिष्टता 'बाधा' केवल लेनदेन के अंत में सत्यापित की जाएगी (यदि SET CONSTRAINTS ALL DEFERRED;इसका उपयोग किया जाता है)।

क्या मेरी धारणा सही है, और यदि ऐसा है, तो क्या अभीष्ट व्यवहार को प्राप्त करने का कोई तरीका है?

धन्यवाद

जवाबों:


15

एक सूचकांक को स्थगित नहीं किया जा सकता है - कोई फर्क नहीं पड़ता कि यह UNIQUEआंशिक है या नहीं, केवल एक UNIQUEबाधा है। बाधाओं के अन्य प्रकार ( FOREIGN KEY, PRIMARY KEY, EXCLUDEलेकिन नहीं -) भी deferrable हैं CHECKकी कमी।

तो अद्वितीय आंशिक सूचकांक (और निहित बाधा यह लागू होता है) हर बयान पर (और वास्तव में हर पंक्ति डालने / चालू कार्यान्वयन में अद्यतन) के बाद चेक किया जाएगा, लेनदेन के अंत में नहीं।


आप क्या कर सकते हैं, यदि आप इस अड़चन को असाध्य के रूप में लागू करना चाहते हैं, तो डिजाइन में एक और तालिका जोड़ना है। कुछ इस तरह:

CREATE TABLE public.booking_status
  ( booking_id int NOT NULL,               -- same types
    check_in timestamp NOT NULL,           -- as in  
    check_out timestamp NOT NULL,          -- booking
    CONSTRAINT unique_booking
        UNIQUE (check_in, check_out)
        DEFERRABLE INITIALLY DEFERRED,
    CONSTRAINT unique_booking_fk
        FOREIGN KEY (booking_id, check_in, check_out)
        REFERENCES public.booking (booking_id, check_in, check_out)
        DEFERRABLE INITIALLY DEFERRED
  ) ;

इस डिजाइन और यह मानकर कि booking_statusइसमें केवल 2 संभावित विकल्प (0 और 1) हैं, आप इसे पूरी तरह से हटा सकते हैं booking(यदि कोई पंक्ति है booking_status, तो यह 1 है, यदि 0 नहीं है)।


एक और तरीका होगा (एब) एक EXCLUDEबाधा का उपयोग करें :

ALTER TABLE booking
    ADD CONSTRAINT unique_booking
        EXCLUDE 
          ( check_in  WITH =, 
            check_out WITH =, 
            (CASE WHEN booking_status = 1 THEN TRUE END) WITH =
          ) 
        DEFERRABLE INITIALLY DEFERRED ;

Dbfiddle पर परीक्षण किया गया

ऊपर क्या करता है:

  • CASEअभिव्यक्ति हो जाता है NULLजब booking_statusनल या से 1. विभिन्न हम लिख सकता है (CASE WHEN booking_status = 1 THEN TRUE END)के रूप में (booking_status = 1 OR NULL)है कि अगर यह बनाता है किसी भी अधिक स्पष्ट।

  • अद्वितीय और बहिष्कृत बाधाएँ उन पंक्तियों को स्वीकार करती हैं जहाँ एक या अधिक भाव NULL हैं। तो यह एक फ़िल्टर किए गए इंडेक्स के रूप में कार्य करता है WHERE booking_status = 1

  • सभी WITHऑपरेटर हैं =इसलिए यह एक UNIQUEबाधा के रूप में कार्य करता है ।

  • ये दोनों संयुक्त बाधा को एक अद्वितीय सूचकांक के रूप में काम करते हैं।

  • लेकिन यह एक अड़चन है और EXCLUDEबाधाओं को टाल दिया जा सकता है।


2
EXCLUDE संस्करण के लिए +1, वही था जिसकी मुझे आवश्यकता थी। यहां EXCLUDE की क्षमताओं को दर्शाने वाला एक और उदाहरण है: cybertec-postgresql.com/en/postgresql-exclude-beyond-unique
बेंजामिन पीटर

(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)के साथ प्रतिस्थापित किया जाना चाहिए ) WHERE (booking_status = 1)क्योंकि "बहिष्करण बाधाएं एक सूचकांक का उपयोग करके कार्यान्वित की जाती हैं", और यह आंशिक सूचकांक WHEREछोटा और तेज़ होगा - postgresql.org/docs/current/sql-createtable.html और postgresql.org/docs/current/sql- createindex.html
डेनिस रेज़कोव

1

यद्यपि इस प्रश्न के वर्ष बीत चुके हैं, मैं स्पैनिश वक्ताओं के लिए स्पष्ट करना चाहूंगा, परीक्षण पोस्टग्रैज में किए गए हैं:

निम्नलिखित बाधा को 1337 रिकॉर्ड की तालिका में जोड़ा गया था, जहां किट प्राथमिक कुंजी है:

**Bloque 1**
ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit) 

यह तालिका के लिए डिफ़ॉल्ट प्राथमिक कुंजी नहीं बनाई जाती है ताकि अगले अद्यतन की कोशिश करते समय हमें त्रुटि मिले:

update ele_kitscompletos
set div_nkit = div_nkit + 1; 

त्रुटि: नकली कुंजी विशिष्टता प्रतिबंध का उल्लंघन करती है «unique_div_nkit»

Postgres में, प्रत्येक ROW के लिए एक UPDATE को निष्पादित करना सत्यापित करता है कि RESTRICTION या CONSTRAINT मिले हैं।


CONSTRAINT IMMEDIATE अब बनाया गया है और प्रत्येक विवरण को अलग से निष्पादित किया गया है:

ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY IMMEDIATE

**Bloque 2**
BEGIN;   
UPDATE ele_kitscompletos set div_nkit = div_nkit + 1;
INSERT INTO public.ele_kitscompletos(div_nkit, otro_campo)
VALUES 
  (1338, '888150502');
COMMIT;

क्वेरी ठीक, 0 पंक्तियाँ प्रभावित (निष्पादन समय: 0 एमएस; कुल समय: 0 एमएस) क्वेरी ठीक, 1328 पंक्तियाँ प्रभावित (निष्पादन समय: 858 एमएस; कुल समय: 858 एमएस) त्रुटि: विलो डुप्लिकेट वियाला restriccios de unicidad «unique_div_nkit» DETAIL : हां अस्तित्व ला लेल्वे (div_nkit) = (1338)।

यहां SI प्राथमिक कुंजी को बदलने की अनुमति देता है क्योंकि यह पूरे पहले पूर्ण वाक्य (1328 पंक्तियों) को निष्पादित करता है; लेकिन यद्यपि यह लेन-देन (BEGIN) में है, CONSTRAINT को COMMIT किए बिना प्रत्येक वाक्य को पूरा करने पर तुरंत मान्य किया जाता है, इसलिए INSERT निष्पादित करते समय त्रुटि उत्पन्न करता है। अंत में हमने CONSTRAINT DEFERRED को निम्न बनाया:

**Bloque 3**
ALTER TABLE public.ele_edivipol
DROP CONSTRAINT unique_div_nkit RESTRICT;   

ALTER TABLE ele_edivipol
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY DEFERRED

यदि हम ** ब्लॉक 2 ** के प्रत्येक कथन को अलग-अलग निष्पादित करते हैं, तो INSERT में कोई भी त्रुटि उत्पन्न नहीं होती है क्योंकि यह मान्य नहीं होता है लेकिन अंतिम COMMIT को निष्पादित किया जाता है जहां यह एक असंगति पाता है।


अंग्रेजी में पूरी जानकारी के लिए मेरा सुझाव है कि आप लिंक की जाँच करें:

डेफ़्रेबल एसक्यूएल बाधाओं को गहराई में

निश्चित रूप से निष्क्रिय बनाम निष्क्रिय नहीं

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