Làm thế nào để thực thi tính duy nhất của khóa chính ghép gồm hai khóa ngoại vào cùng một bảng?


7

Tôi có hai bảng: một userbảng và một friendshipbảng. Nói friendshipbảng trông như thế này:

friendship
------------
user_one_id
user_two_id
other_fields

Tôi cần phải thực thi tính duy nhất của sự kết hợp các giá trị (user_one_id, user_two_id)và bỏ qua thứ tự , vì vậy:

user_one_id | user_two_id
------------+------------
          1 |          2
          2 |          1  -- the DBMS should throw a unique constraint error when trying to insert a row like this one.

Một điểm quan trọng là user_one_idđại diện cho người khởi xướng của friendshipuser_two_idđại diện cho người nhận , vì vậy tôi không thể chỉ làm cho "nhỏ" của hai id user_one_id .

Câu hỏi

Có cách nào để làm điều này bằng cách sử dụng các ràng buộc hoặc tôi nên thực hiện cách này theo cách khác?

Câu trả lời:


8

Theo nhận xét của bạn,

Đối với trường hợp sử dụng của tôi, user_one_id đại diện cho người khởi xướng tình bạn và user_two_id đại diện cho người nhận tình bạn. Vì vậy, tôi không thể chỉ sử dụng giá trị thấp nhất là user_one_id.

Vâng, bạn vẫn có thể làm điều đó. Trường hợp sử dụng của bạn chỉ loại trừ ràng buộc hàng để đảm bảo điều đó. Những gì bạn muốn là sử dụng một ràng buộc bảng, một cái gì đó như thế này.

CREATE TABLE friendship (
  user_one_id int NOT NULL,
  user_two_id int NOT NULL,
  CHECK (user_one_id != user_two_id ),
  PRIMARY KEY (user_one_id, user_two_id)
);
-- you can do least first if you want. doesn't matter.
CREATE UNIQUE INDEX ON friendship (
  greatest(user_one_id, user_two_id),
  least(user_one_id, user_two_id)
);

Chúng tôi có rất nhiều thứ đang diễn ra ở đây .. Chúng tôi đảm bảo.

  1. Cả hai đều NOT NULL
  2. Cả hai không bằng nhau
  3. Cả hai đều UNIQUE (user_one_id, user_two_id)

Điều đó để lại một vấn đề còn tồn tại về tính duy nhất giao hoán, chúng tôi giải quyết vấn đề đó bằng một ràng buộc bảng duy nhất tùy chỉnh được thực hiện với một chỉ mục.

Bằng chứng trong bánh pudding

INSERT INTO friendship VALUES ( 1,2 );
INSERT 0 1
test=# INSERT INTO friendship VALUES ( 2,1 );
ERROR:  duplicate key value violates unique constraint friendship_greatest_least_idx"
DETAIL:  Key ((GREATEST(user_one_id, user_two_id)), (LEAST(user_one_id, user_two_id)))=(2, 1) already exists.

Như một lưu ý thân thiện quan trọng, tên của bạn là tất cả các loại ngớ ngẩn. Các mối quan hệ là tốt. Trong sản xuất, xin vui lòng cho họ tên tốt hơn ..

friendship
----------
request_initiator
request_target
other_fields

Tuy nhiên, giải pháp chính xác, có một chi tiết nhỏ có thể được cải thiện: giải pháp của bạn tạo ra hai chỉ mục trong nền, trong đó một chỉ số sẽ đủ. Tạo bảng không có khóa chính, sau đó lập chỉ mục (GREATEST (), LEAST ()) và thực hiện ALTER TABLE ADD PRIMary KEY SỬ DỤNG INDEX ...
Twinkles

2
Mặc dù, bây giờ tôi nghĩ về nó, hầu hết các cơ sở dữ liệu trong thế giới thực có thể sẽ được hưởng lợi từ chỉ số thứ hai.
Twinkles

@Twinkles Điều này không hoạt động, gây ra lỗi:ERROR: index "friendship_greatest_least_idx" contains expressions LINE 1: alter table friendship add primary key using index friendshi... ^ DETAIL: Cannot create a primary key or unique constraint using such an index
jsvisa

0

Giải pháp rất tốt. Nhưng trước tiên chúng ta nên đảm bảo rằng không có số trùng nhau trong hai cột. tức là., một số sẽ là duy nhất cho một cột.

Tôi muốn đề xuất giải pháp đơn giản sau:

tạo hai chuỗi:

1.

user_one_id_seq: với số chẵn

user_two_id_seq với số lẻ

2.

user_one_id_seq: các số từ 1 đến 10000

số user_two_id_seq trong khoảng từ 10001 đến 20000


0

Thiết kế nên được chuẩn hóa bằng cách tạo một bảng giao nhau có tên user_friendship:

tạo bảng user_friendship (user id int NOT NULL, tình bạn id int NOT NULL, PRIMARY KEY (user_id, friendship_id), FOREIGN KEY friendship_id Tài liệu tham khảo hữu nghị (friendship_id), FOREIGN KEY user_id Tài liệu tham khảo sử dụng (user_id)
);


-1

Nếu bạn đặt logic để đặt giá trị của user_one_id thấp hơn giá trị của user_two_id , bạn sẽ không gặp vấn đề gì.


2
Đối với trường hợp sử dụng của tôi, user_one_idđại diện cho người khởi xướng tình bạn, và user_two_idđại diện cho người nhận tình bạn. Vì vậy, tôi không thể chỉ sử dụng giá trị thấp nhất là user_one_id.
maniciam

1
@maniciam xem câu trả lời của tôi.
Evan Carroll
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.