अनन्य अवरोध बनाम अनुक्रमणिका पोस्टग्रैट्स


157

जैसा कि मैं समझ सकता हूँ कि निम्नलिखित परिभाषाएँ दस्तावेज़ीकरण समतुल्य हैं:

create table foo (
    id serial primary key,
    code integer,
    label text,
    constraint foo_uq unique (code, label));

create table foo (
    id serial primary key,
    code integer,
    label text);
create unique index foo_idx on foo using btree (code, label);    

हालाँकि, Postgres 9.4 के लिए मैनुअल में एक नोट कहता है:

तालिका में एक अद्वितीय बाधा जोड़ने का पसंदीदा तरीका है ALTER TABLE ... ADD CONSTRAINT। अद्वितीय बाधाओं को लागू करने के लिए अनुक्रमित के उपयोग को एक कार्यान्वयन विवरण माना जा सकता है जिसे सीधे एक्सेस नहीं किया जाना चाहिए।

(संपादित करें: यह नोट मैनुअल से पोस्टग्रेज 9.5 के साथ हटा दिया गया था।)

क्या यह केवल अच्छी शैली की बात है? इनमें से किसी एक विकल्प (उदाहरण में प्रदर्शन) के व्यावहारिक परिणाम क्या हैं?


23
(केवल) व्यावहारिक अंतर यह है कि आप एक अद्वितीय बाधा के लिए एक विदेशी कुंजी बना सकते हैं, लेकिन एक अद्वितीय सूचकांक के लिए नहीं।
a_horse_with_no_name

29
एक अन्य तरीके से लाभ उठाएं ( जैसा कि हाल ही में एक और प्रश्न में आया है ) यह है कि आपके पास एक आंशिक अद्वितीय सूचकांक हो सकता है, जैसे कि "यूनिक (फू) जहां बार नाल है"। AFAIK, एक बाधा के साथ ऐसा करने का कोई तरीका नहीं है।
IMSoP

3
@a_horse_with_no_name मुझे यकीन नहीं है कि ऐसा कब हुआ, लेकिन यह अब सच नहीं लगता है। यह SQL फिडेल एक अद्वितीय सूचकांक के लिए विदेशी कुंजी संदर्भों की अनुमति देता है: sqlfiddle.com/# -17 / 20ee9 ; संपादित करें: अद्वितीय अनुक्रमणिका में 'फ़िल्टर' जोड़ने से यह काम करना बंद कर देता है (जैसा कि अपेक्षित है)
user1935361

1
डाक्यूमेंट्स से डाक्यूमेंटेशन: जब एक अद्वितीय बाधा या प्राथमिक कुंजी को एक टेबल के लिए परिभाषित किया जाता है, तो पोस्टग्रेक्यूएल स्वचालित रूप से एक अद्वितीय सूचकांक बनाता है। postgresql.org/docs/9.4/static/indexes-unique.html
maggu

मैं @ user1935361 के साथ सम्‍मिलित हूं, यदि किसी अनूठे इंडेक्स की विदेशी कुंजी बनाना संभव नहीं था (कम से कम पीजी 10 के साथ) तो मैं इस मुद्दे पर एक लंबे समय से पहले चल सकता था।
एंडी

जवाबों:


132

मुझे इस बुनियादी लेकिन महत्वपूर्ण मुद्दे पर कुछ संदेह था, इसलिए मैंने उदाहरण के द्वारा सीखने का फैसला किया।

के परीक्षण तालिका बनाएँ मास्टर दो स्तंभों के साथ, con_id अद्वितीय बाधा के साथ और ind_id अद्वितीय सूचकांक द्वारा अनुक्रमित।

create table master (
    con_id integer unique,
    ind_id integer
);
create unique index master_unique_idx on master (ind_id);

    Table "public.master"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Indexes:
    "master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
    "master_unique_idx" UNIQUE, btree (ind_id)

तालिका विवरण (psql में \ d) में आप अद्वितीय सूचकांक से अद्वितीय अवरोध बता सकते हैं।

विशिष्टता

चलो विशिष्टता की जांच करें, बस मामले में।

test=# insert into master values (0, 0);
INSERT 0 1
test=# insert into master values (0, 1);
ERROR:  duplicate key value violates unique constraint "master_con_id_key"
DETAIL:  Key (con_id)=(0) already exists.
test=# insert into master values (1, 0);
ERROR:  duplicate key value violates unique constraint "master_unique_idx"
DETAIL:  Key (ind_id)=(0) already exists.
test=#

यह उम्मीद के मुताबिक काम करता है!

विदेशी कुंजी

अब हम मास्टर में हमारे दो स्तंभों के संदर्भ में दो विदेशी कुंजियों के साथ विस्तार तालिका को परिभाषित करेंगे ।

create table detail (
    con_id integer,
    ind_id integer,
    constraint detail_fk1 foreign key (con_id) references master(con_id),
    constraint detail_fk2 foreign key (ind_id) references master(ind_id)
);

    Table "public.detail"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Foreign-key constraints:
    "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
    "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)

खैर, कोई त्रुटि नहीं। चलो यह सुनिश्चित करें कि यह काम करता है।

test=# insert into detail values (0, 0);
INSERT 0 1
test=# insert into detail values (1, 0);
ERROR:  insert or update on table "detail" violates foreign key constraint "detail_fk1"
DETAIL:  Key (con_id)=(1) is not present in table "master".
test=# insert into detail values (0, 1);
ERROR:  insert or update on table "detail" violates foreign key constraint "detail_fk2"
DETAIL:  Key (ind_id)=(1) is not present in table "master".
test=#

दोनों स्तंभों को विदेशी कुंजियों में संदर्भित किया जा सकता है।

सूचकांक का उपयोग करते हुए बाधा

आप मौजूदा अद्वितीय अनुक्रमणिका का उपयोग करके तालिका बाधा जोड़ सकते हैं।

alter table master add constraint master_ind_id_key unique using index master_unique_idx;

    Table "public.master"
 Column |  Type   | Modifiers
--------+---------+-----------
 con_id | integer |
 ind_id | integer |
Indexes:
    "master_con_id_key" UNIQUE CONSTRAINT, btree (con_id)
    "master_ind_id_key" UNIQUE CONSTRAINT, btree (ind_id)
Referenced by:
    TABLE "detail" CONSTRAINT "detail_fk1" FOREIGN KEY (con_id) REFERENCES master(con_id)
    TABLE "detail" CONSTRAINT "detail_fk2" FOREIGN KEY (ind_id) REFERENCES master(ind_id)

अब कॉलम की बाधाओं के विवरण में कोई अंतर नहीं है।

आंशिक सूचकांक

तालिका बाधा घोषणा में आप आंशिक अनुक्रमणिका नहीं बना सकते। यह सीधे की परिभाषा से आता है create table ...। अद्वितीय सूचकांक घोषणा में आप WHERE clauseआंशिक सूचकांक बनाने के लिए सेट कर सकते हैं । आप एक्सप्रेशन (केवल कॉलम पर) पर इंडेक्स भी बना सकते हैं और कुछ अन्य मापदंडों (कॉलेशन, सॉर्ट ऑर्डर, NULLs प्लेसमेंट) को परिभाषित कर सकते हैं।

आप आंशिक अनुक्रमणिका का उपयोग करके तालिका बाधा नहीं जोड़ सकते

alter table master add column part_id integer;
create unique index master_partial_idx on master (part_id) where part_id is not null;

alter table master add constraint master_part_id_key unique using index master_partial_idx;
ERROR:  "master_partial_idx" is a partial index
LINE 1: alter table master add constraint master_part_id_key unique ...
                               ^
DETAIL:  Cannot create a primary key or unique constraint using such an index.

क्या यह वास्तविक जानकारी है? विशेष रूप से आंशिक अनुक्रमों के बारे में
एनाटोल

1
@anatol - हाँ, यह है।
क्लिन

30

UNIQUE INDEXबनाम का उपयोग करने का एक और लाभ UNIQUE CONSTRAINTयह है कि आप आसानी से DROP/ CREATEएक सूचकांक कर सकते हैं CONCURRENTLY, जबकि एक बाधा के साथ आप नहीं कर सकते।


4
AFAIK समवर्ती एक अद्वितीय सूचकांक ड्रॉप करना संभव नहीं है। postgresql.org/docs/9.3/static/sql-dropindex.html "इस विकल्प का उपयोग करते समय जागरूक होने के लिए कई चेतावनी हैं। केवल एक सूचकांक नाम निर्दिष्ट किया जा सकता है, और CASCADE विकल्प समर्थित नहीं है (इस प्रकार, एक सूचकांक) यह एक UNIQUE या प्राथमिक कुंजी बाधा का समर्थन करता है जिसे इस तरह से नहीं छोड़ा जा सकता है।) "
Rafł Cie 8lak

15

विशिष्टता एक अड़चन है। यह एक अद्वितीय सूचकांक के निर्माण के माध्यम से कार्यान्वित होने के लिए होता है क्योंकि एक सूचकांक एक निर्धारित मूल्य मौजूद है या नहीं यह निर्धारित करने के लिए एक सूचकांक जल्दी से सभी मौजूदा मूल्यों की खोज करने में सक्षम है।

वैचारिक रूप से सूचकांक एक कार्यान्वयन विवरण है और विशिष्टता केवल बाधाओं से जुड़ी होनी चाहिए।

पूरा पाठ

इसलिए गति प्रदर्शन समान होना चाहिए


4

मेरे पास एक और बात यह है कि आप अनोखे इंडेक्स में एसक्यूएल के भावों का उपयोग कर सकते हैं, लेकिन बाधाओं में नहीं।

तो, यह काम नहीं करता है:

CREATE TABLE users (
    name text,
    UNIQUE (lower(name))
);

लेकिन निम्नलिखित काम करता है।

CREATE TABLE users (
    name text
);
CREATE UNIQUE INDEX uq_name on users (lower(name));

मैं citextएक्सटेंशन का उपयोग करूंगा ।
छत

@ इसे प्रयोग मामले पर निर्भर करता है। कभी-कभी आप केस-असंवेदनशील विशिष्टता को सुनिश्चित करते हुए आवरण को संरक्षित करना चाहते हैं
सैम्पसन क्रॉले

2

चूंकि विभिन्न लोगों ने अद्वितीय बाधाओं पर अद्वितीय अनुक्रमितों के फायदे प्रदान किए हैं, इसलिए यहां एक खामी है: एक अद्वितीय बाधा को स्थगित किया जा सकता है (केवल लेनदेन के अंत में जांच की गई), एक अद्वितीय सूचकांक नहीं हो सकता है।


यह कैसे हो सकता है, यह देखते हुए कि सभी अद्वितीय बाधाओं में एक अद्वितीय सूचकांक है?
क्रिस

1
क्योंकि अनुक्रमणिकाओं में डिफरेंसिंग के लिए एपीआई नहीं होता है, केवल बाधाएं होती हैं, इसलिए जब अद्वितीय बाधा का समर्थन करने के लिए कवर के नीचे डिफरल मशीनरी मौजूद होती है, तो इंडेक्स को डिफ्रैबल घोषित करने या इसे स्थगित करने का कोई तरीका नहीं है।
मस्कलिन

0

मैंने इसे डॉक में पढ़ा:

ADD table_constraint [वैध नहीं]

यह फ़ॉर्म उसी सिंटैक्स का उपयोग करके तालिका में एक नया अवरोध जोड़ता है CREATE TABLE, साथ ही विकल्प NOT VALID, जो वर्तमान में केवल विदेशी कुंजी बाधाओं के लिए अनुमत है। यदि बाधा को चिह्नित किया गया है NOT VALID, तो यह सत्यापित करने के लिए कि प्रारंभिक-लंबी जाँच करें कि तालिका की सभी पंक्तियाँ संतुष्ट करती हैं कि बाधा को छोड़ दिया गया है । बाधा अभी भी बाद के आवेषण या अपडेट के खिलाफ लागू की जाएगी (अर्थात, वे विफल हो जाएंगे जब तक कि संदर्भित तालिका में एक मिलान पंक्ति नहीं है)। लेकिन डेटाबेस यह नहीं मानेगा कि बाधा तालिका में सभी पंक्तियों के लिए है, जब तक कि इसे वैध करार विकल्प का उपयोग करके मान्य नहीं किया जाता है।

तो मुझे लगता है कि इसे आप एक बाधा डालकर "आंशिक विशिष्टता" कहते हैं।

और, विशिष्टता कैसे सुनिश्चित करें:

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

नोट: किसी तालिका में एक अद्वितीय बाधा जोड़ने का पसंदीदा तरीका ALTER TABLE ... ADD CONSTRAINT है। अद्वितीय बाधाओं को लागू करने के लिए अनुक्रमित के उपयोग को एक कार्यान्वयन विवरण माना जा सकता है जिसे सीधे एक्सेस नहीं किया जाना चाहिए। हालांकि, एक को पता होना चाहिए कि अद्वितीय स्तंभों पर मैन्युअल रूप से अनुक्रमित बनाने की कोई आवश्यकता नहीं है; ऐसा करने से स्वचालित रूप से बनाए गए इंडेक्स की नक़ल होगी।

अतः हमें अनूठेपन को सुनिश्चित करने के लिए अवरोध पैदा करना चाहिए, जो एक सूचकांक बनाता है।

मैं इस समस्या को कैसे देखता हूँ?

एक "बाधा" उद्देश्य के लिए gramatically सुनिश्चित करना है कि इस स्तंभ अद्वितीय होना चाहिए, यह एक कानून, एक नियम स्थापित करता है; जबकि "इंडेक्स" शब्दार्थिक है , "कैसे लागू किया जाए, कैसे विशिष्टता प्राप्त की जाए, जब इसे लागू करने की बात आती है तो इसका क्या मतलब है"। तो, जिस तरह से Postgresql इसे लागू करता है, वह बहुत तार्किक है: सबसे पहले, आप घोषणा करते हैं कि एक कॉलम अद्वितीय होना चाहिए, फिर, Postgresql आपके लिए एक अद्वितीय सूचकांक जोड़ने के कार्यान्वयन को जोड़ता है


1
"तो मुझे लगता है कि इसे आप एक बाधा डालकर" आंशिक विशिष्टता "कहते हैं।" अनुक्रमित whereखंड के माध्यम से रिकॉर्ड के केवल एक अच्छी तरह से परिभाषित सबसेट पर लागू हो सकते हैं , इसलिए आप यह परिभाषित कर सकते हैं कि रिकॉर्ड अद्वितीय हैं IFF वे कुछ मानदंडों को पूरा करते हैं। यह आसानी से अभिलेखों के एक अपरिभाषित सेट के लिए अवरोधों को निष्क्रिय कर देता है जो बाधा बनने से पहले होता है। यह पूरी तरह से अलग है, और बाद वाला काफी कम उपयोगी है, हालांकि मुझे लगता है कि प्रगतिशील पलायन के लिए यह सुविधाजनक है।
मस्कलिन

0

ताला लगाने में अंतर है।
एक इंडेक्स जोड़ने से तालिका तक पहुंच को ब्लॉक नहीं किया जाता है।
बाधा जोड़ने से एक टेबल लॉक लगाया जाता है (इसलिए सभी चयन अवरुद्ध होते हैं) क्योंकि इसे ALTER TABLE के माध्यम से जोड़ा जाता है ।


0

एक बहुत मामूली चीज जो केवल बाधाओं के साथ की जा सकती है और अनुक्रमित के साथ नहीं, ON CONFLICT ON CONSTRAINTखंड का उपयोग कर रही है ( यह प्रश्न भी देखें )।

यह काम नहीं करता है:

CREATE TABLE T (a INT PRIMARY KEY, b INT, c INT);
CREATE UNIQUE INDEX u ON t(b);

INSERT INTO T (a, b, c)
VALUES (1, 2, 3)
ON CONFLICT ON CONSTRAINT u
DO UPDATE SET c = 4
RETURNING *;

यह उत्तपन करता है:

[42704]: ERROR: constraint "u" for table "t" does not exist

सूचकांक को एक बाधा में बदलें:

DROP INDEX u;
ALTER TABLE t ADD CONSTRAINT u UNIQUE (b);

और INSERTबयान अब काम करता है।

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