Postgres ข้อ จำกัด ที่ไม่ซ้ำกับดัชนี


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ที่ไม่ซ้ำกันในตารางเป็น การใช้ดัชนีเพื่อบังคับใช้ข้อ จำกัด ที่ไม่เหมือนใครอาจถูกพิจารณาว่าเป็นรายละเอียดการนำไปปฏิบัติซึ่งไม่สามารถเข้าถึงได้โดยตรง

(แก้ไข: บันทึกนี้ถูกลบออกจากคู่มือที่มี Postgres 9.5)

มันเป็นเพียงเรื่องของสไตล์ที่ดีหรือไม่? อะไรคือผลที่เกิดขึ้นจริงของการเลือกหนึ่งในตัวแปรเหล่านี้ (เช่นในประสิทธิภาพ)


23
ความแตกต่างในทางปฏิบัติ (เท่านั้น) คือคุณสามารถสร้างคีย์ต่างประเทศให้กับข้อ จำกัด ที่เป็นเอกลักษณ์ แต่ไม่สามารถสร้างดัชนีที่ไม่ซ้ำกันได้
a_horse_with_no_name

29
ข้อได้เปรียบในทางกลับกัน ( ดังที่เกิดขึ้นในคำถามอื่นเมื่อเร็ว ๆ นี้ ) คือคุณสามารถมีดัชนีที่ไม่ซ้ำกันบางส่วนเช่น "ไม่ซ้ำกัน (foo) ตำแหน่งที่แถบว่างอยู่" AFAIK ไม่มีทางที่จะทำเช่นนั้นด้วยข้อ จำกัด
IMSoP

3
@a_horse_with_no_name ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้น แต่สิ่งนี้ดูเหมือนจะไม่เป็นจริงอีกต่อไป ซอ SQL นี้ช่วยให้การอ้างอิงคีย์ต่างประเทศไปยังดัชนีที่ไม่ซ้ำกัน: sqlfiddle.com/#!17/20ee9 ; แก้ไข: การเพิ่ม 'ตัวกรอง' ในดัชนีเฉพาะทำให้สิ่งนี้หยุดทำงาน (ตามที่คาดไว้)
user1935361

1
จากเอกสารประกอบของ postgres: PostgreSQL จะสร้างดัชนีที่ไม่ซ้ำกันโดยอัตโนมัติเมื่อมีการกำหนดข้อ จำกัด หรือคีย์หลักเฉพาะสำหรับตาราง postgresql.org/docs/9.4/static/indexes-unique.html
maggu

ฉันเห็นด้วยกับ @ user1935361 ถ้ามันเป็นไปไม่ได้ที่จะสร้างคีย์ต่างประเทศให้กับดัชนีที่ไม่ซ้ำกัน (ด้วย PG 10 อย่างน้อย) ฉันจะได้พบกับปัญหานี้มานานแล้ว
Andy

คำตอบ:


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)

ในคำอธิบายตาราง (\ d ใน psql) คุณสามารถบอกข้อ จำกัด ที่ไม่ซ้ำจากดัชนีที่ไม่ซ้ำกัน

ความเป็นเอกลักษณ์

ตรวจสอบความเป็นเอกลักษณ์กันเถอะ

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ให้สร้างดัชนีบางส่วน นอกจากนี้คุณยังสามารถสร้างดัชนีบนนิพจน์ (ไม่เพียง แต่ในคอลัมน์) และกำหนดพารามิเตอร์อื่น ๆ (การเรียงลำดับการเรียงลำดับการจัดตำแหน่ง NULL)

คุณไม่สามารถเพิ่มข้อ จำกัด ของตารางโดยใช้ดัชนีบางส่วน

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.

เป็นข้อมูลจริงเหรอ โดยเฉพาะอย่างยิ่งเกี่ยวกับดัชนีบางส่วน
Anatol

1
@anatol - ใช่มันเป็น
klin

30

ข้อดีอีกข้อหนึ่งของการใช้UNIQUE INDEXvs. UNIQUE CONSTRAINTคือคุณสามารถDROP/ CREATEดัชนีได้อย่างง่ายดายCONCURRENTLYในขณะที่มีข้อ จำกัด ที่คุณไม่สามารถทำได้


4
AFAIK ไม่สามารถวางดัชนีที่ไม่ซ้ำพร้อมกันได้ postgresql.org/docs/9.3/static/sql-dropindex.html "มีข้อควรระวังหลายประการที่ต้องระวังเมื่อใช้ตัวเลือกนี้สามารถระบุชื่อดัชนีได้เพียงชื่อเดียวเท่านั้นและไม่สนับสนุนตัวเลือก CASCADE (ดังนั้นดัชนี ที่รองรับข้อ จำกัด UNIQUE หรือคีย์หลักไม่สามารถทำแบบนี้ได้) "
RafałCieślak

15

เอกลักษณ์เป็นข้อ จำกัด มันเกิดขึ้นที่จะดำเนินการผ่านการสร้างดัชนีที่ไม่ซ้ำกันเนื่องจากดัชนีสามารถค้นหาค่าที่มีอยู่ทั้งหมดได้อย่างรวดเร็วเพื่อตรวจสอบว่ามีค่าที่กำหนดอยู่แล้ว

แนวคิดดัชนีเป็นรายละเอียดการดำเนินงานและความเป็นเอกลักษณ์ควรเกี่ยวข้องกับข้อ จำกัด เท่านั้น

ข้อความเต็ม

ดังนั้นประสิทธิภาพความเร็วควรเท่ากัน


4

อีกสิ่งที่ฉันได้พบคือคุณสามารถใช้นิพจน์ sql ในดัชนีเฉพาะ แต่ไม่ จำกัด

ดังนั้นวิธีนี้ใช้ไม่ได้:

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

แต่งานต่อไปนี้

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

ฉันจะใช้citextส่วนขยาย
เซเว่น

@ รับมันขึ้นอยู่กับกรณีการใช้งาน บางครั้งคุณต้องการเก็บรักษากล่องหุ้มในขณะที่ทำให้แน่ใจว่ามีตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
Sampson Crowley

2

เนื่องจากบุคคลต่าง ๆ ให้ข้อดีของดัชนีที่ไม่ซ้ำกันมากกว่าข้อ จำกัด ที่ไม่ซ้ำกันนี่คือข้อเสียเปรียบ: ข้อ จำกัด ที่ไม่ซ้ำกันสามารถเลื่อนได้ (ตรวจสอบเฉพาะตอนท้ายธุรกรรม) ดัชนีที่ไม่ซ้ำกันจึงไม่สามารถทำได้


สิ่งนี้จะเป็นไปได้อย่างไรเนื่องจากข้อ จำกัด ที่ไม่ซ้ำกันทั้งหมดมีดัชนีที่ไม่ซ้ำกัน
Chris

1
เนื่องจากดัชนีไม่มี API สำหรับการเลื่อนเวลามีข้อ จำกัด เพียงอย่างเดียวดังนั้นในขณะที่กลไกการเลื่อนเวลาอยู่ภายใต้ฝาครอบเพื่อรองรับข้อ จำกัด ที่ไม่ซ้ำกันจึงไม่มีวิธีประกาศดัชนีที่เลื่อนออกไปหรือเลื่อนออกไป
Masklinn

0

ฉันอ่านสิ่งนี้ในเอกสาร:

เพิ่ม table_constraint [ไม่ถูกต้อง]

แบบฟอร์มนี้เพิ่มข้อ จำกัด ใหม่ให้กับตารางโดยใช้ไวยากรณ์เดียวCREATE TABLEกับรวมถึงตัวเลือกNOT VALIDซึ่งขณะนี้ได้รับอนุญาตสำหรับข้อ จำกัด คีย์ต่างประเทศเท่านั้น หากข้อ จำกัด ที่มีการทำเครื่องหมายNOT VALIDการตรวจสอบเบื้องต้นอาจ-ยาวเพื่อตรวจสอบว่าทุกแถวในตารางตอบสนองข้อ จำกัด คือข้าม ข้อ จำกัด จะยังคงมีผลบังคับใช้กับส่วนแทรกหรือการปรับปรุงที่ตามมา (นั่นคือพวกเขาจะล้มเหลวเว้นแต่จะมีแถวที่ตรงกันในตารางอ้างอิง) แต่ฐานข้อมูลจะไม่ถือว่าข้อ จำกัด มีไว้สำหรับแถวทั้งหมดในตารางจนกว่าจะมีการตรวจสอบความถูกต้องโดยใช้ตัวเลือก VALIDATE CONSTRAINT

ดังนั้นฉันคิดว่ามันเป็นสิ่งที่คุณเรียกว่า "ความเป็นเอกลักษณ์บางส่วน" โดยการเพิ่มข้อ จำกัด

และเกี่ยวกับวิธีการรับประกันความเป็นเอกลักษณ์:

การเพิ่มข้อ จำกัด ที่ไม่ซ้ำกันจะสร้างดัชนี B-tree ที่ไม่ซ้ำกันโดยอัตโนมัติในคอลัมน์หรือกลุ่มของคอลัมน์ที่ระบุไว้ในข้อ จำกัด ข้อ จำกัด ที่ไม่ซ้ำกันที่ครอบคลุมเฉพาะบางแถวไม่สามารถเขียนเป็นข้อ จำกัด ที่ไม่ซ้ำกัน แต่เป็นไปได้ที่จะบังคับใช้ข้อ จำกัด ดังกล่าวโดยการสร้างดัชนีบางส่วนที่ไม่ซ้ำกัน

หมายเหตุ: วิธีที่ต้องการเพิ่มข้อ จำกัด ที่ไม่ซ้ำกันในตารางคือ ALTER TABLE … ADD CONSTRAINT การใช้ดัชนีเพื่อบังคับใช้ข้อ จำกัด ที่ไม่เหมือนใครอาจถูกพิจารณาว่าเป็นรายละเอียดการนำไปปฏิบัติซึ่งไม่สามารถเข้าถึงได้โดยตรง อย่างไรก็ตามหนึ่งควรทราบว่าไม่จำเป็นต้องสร้างดัชนีในคอลัมน์ที่ไม่ซ้ำกันด้วยตนเอง; การทำเช่นนั้นจะเป็นการทำซ้ำดัชนีที่สร้างขึ้นโดยอัตโนมัติ

ดังนั้นเราควรเพิ่มข้อ จำกัด ซึ่งสร้างดัชนีเพื่อให้แน่ใจว่ามีเอกลักษณ์

ฉันเห็นปัญหานี้อย่างไร

"ข้อ จำกัด " มีจุดมุ่งหมายเพื่อให้แน่ใจได้อย่างน่าทึ่งว่าคอลัมน์นี้ควรไม่ซ้ำกันมันสร้างกฎหมายกฎ; ในขณะที่ "ดัชนี" หมายถึงความหมายเกี่ยวกับ "วิธีการนำไปใช้งานวิธีการทำให้เกิดความเป็นเอกลักษณ์ ดังนั้นวิธีการที่ใช้ PostgreSQL มันเป็นตรรกะมาก: ครั้งแรกที่คุณประกาศว่าคอลัมน์ต้องไม่ซ้ำกันแล้ว PostgreSQL เพิ่มการดำเนินงานของการเพิ่มดัชนีที่ไม่ซ้ำสำหรับคุณ


1
"ดังนั้นฉันคิดว่ามันเป็นสิ่งที่คุณเรียกว่า" ความเป็นเอกลักษณ์บางส่วน "โดยการเพิ่มข้อ จำกัด " ดัชนีสามารถนำไปใช้กับชุดย่อยที่กำหนดไว้อย่างดีของระเบียนผ่านส่วนwhereคำสั่งเท่านั้นดังนั้นคุณสามารถกำหนดว่าระเบียนเป็น IFF ที่ไม่ซ้ำกันซึ่งเป็นไปตามเกณฑ์บางประการ นี่เป็นการปิดการใช้งานข้อ จำกัด สำหรับชุดของเร็กคอร์ดที่ไม่ได้กำหนดซึ่งจะสร้างข้อ จำกัด ก่อนวันที่ มันแตกต่างอย่างสิ้นเชิงและอย่างหลังมีประโยชน์น้อยกว่าอย่างมากถึงแม้ว่ามันจะสะดวกสำหรับการโยกย้ายแบบก้าวหน้าที่ฉันเดา
Masklinn

0

มีความแตกต่างในการล็อค
การเพิ่มดัชนีไม่บล็อกการเข้าถึงการอ่านไปยังตาราง
การเพิ่มข้อ จำกัด ไม่ใส่ล็อคตาราง (เพื่อเลือกทั้งหมดจะถูกบล็อก) เพราะมันจะถูกเพิ่มผ่านเปลี่ยนแปลงตาราง


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คำสั่งในขณะนี้ทำงาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.