Những gì bạn đã thiết kế là tốt. Những gì cần được thêm vào là một ràng buộc để làm cho mối quan hệ trở nên vô hướng. Vì vậy, bạn không thể có một (1,5)
hàng mà không có một (5,1)
hàng được thêm vào.
Điều này có thể được thực hiện * với một ràng buộc tự tham chiếu trên bảng cầu.
*: có thể được thực hiện trong Postgres, Oracle, DB2 và tất cả DBMS đã thực hiện các ràng buộc khóa ngoài như tiêu chuẩn SQL mô tả (hoãn lại, ví dụ: đã kiểm tra ở cuối giao dịch.) Máy chủ kiểm tra chúng ở cuối câu lệnh và cấu trúc này vẫn hoạt động. Bạn không thể thực hiện điều này trong MySQL vì "InnoDB kiểm tra các ràng buộc UNIITE và FOREIGN KEY theo từng hàng" .
Vì vậy, trong Postgres, những điều sau đây sẽ phù hợp với yêu cầu của bạn:
CREATE TABLE x
(
x_id SERIAL NOT NULL PRIMARY KEY,
data VARCHAR(10) NOT NULL
);
CREATE TABLE bridge_x
(
x_id1 INTEGER NOT NULL REFERENCES x (x_id),
x_id2 INTEGER NOT NULL REFERENCES x (x_id),
PRIMARY KEY(x_id1, x_id2),
CONSTRAINT x_x_directionless
FOREIGN KEY (x_id2, x_id1)
REFERENCES bridge_x (x_id1, x_id2)
);
Đã thử nghiệm tại: SQL-Fiddle
Nếu bạn cố gắng thêm một hàng (1,5)
:
INSERT INTO bridge_x VALUES
(1,5) ;
Nó thất bại với:
LỖI: chèn hoặc cập nhật trên bảng "bridge_x" vi phạm chính nước ngoài hạn chế "x_x_directionless"
Chi tiết: Key (x_id2, x_id1) = (5, 1) là không có mặt trong bảng "bridge_x" .:
INSERT INTO bridge_x GIÁ TRỊ (1,5)
Ngoài ra, bạn có thể thêm một CHECK
ràng buộc nếu bạn muốn cấm (y,y)
các hàng:
ALTER TABLE bridge_x
ADD CONSTRAINT x_x_self_referencing_items_not_allowed
CHECK (x_id1 <> x_id2) ;
Có nhiều cách khác để thực hiện điều này như bạn đề cập, như chỉ lưu trữ một hướng của mối quan hệ (trong một hàng chứ không phải hai) bằng cách buộc id thấp hơn x_id1
và id cao hơn trong x_id2
cột. Có vẻ dễ thực hiện hơn, nhưng thường dẫn đến các truy vấn phức tạp hơn sau này:
CREATE TABLE bridge_x
(
x_id1 INTEGER NOT NULL REFERENCES x (x_id),
x_id2 INTEGER NOT NULL REFERENCES x (x_id),
PRIMARY KEY(x_id1, x_id2),
CONSTRAINT x_x_directionless
CHECK (x_id1 <= x_id2) -- or "<" to forbid `(y,y)` rows
);