Cách liên kết hai hàng trong cùng một bảng


11

Tôi có một bảng trong đó các hàng có thể liên quan với nhau và về mặt logic, mối quan hệ đi cả hai chiều (về cơ bản là không có hướng) giữa hai hàng. (Và nếu bạn đang tự hỏi, vâng, đây thực sự nên là một bảng. Đây là hai điều có cùng loại / thực thể logic chính xác.) Tôi có thể nghĩ ra một vài cách để thể hiện điều này:

  1. Lưu trữ mối quan hệ và đảo ngược của nó
  2. Lưu trữ mối quan hệ theo một cách, hạn chế cơ sở dữ liệu lưu trữ theo cách khác và có hai chỉ mục với các thứ tự ngược lại cho FK (một chỉ mục là chỉ mục PK)
  3. Lưu trữ mối quan hệ một chiều với hai chỉ mục và cho phép chèn thứ hai bằng mọi cách (nghe có vẻ xui xẻo, nhưng này, tính đầy đủ)
  4. Tạo một số loại bảng nhóm và có FK trên bảng gốc với nó. (Đặt ra nhiều câu hỏi. Bảng nhóm sẽ chỉ có một số; tại sao thậm chí có bảng? Tạo FK NULLable hoặc có các nhóm có một hàng liên kết?)

Một số ưu và nhược điểm chính của những cách này là gì, và tất nhiên, có cách nào tôi chưa nghĩ đến?

Đây là một SQLFiddle để chơi với: http://sqlfiddle.com/#!12/7ee1a/1/0 . .


Một giá trị nhất định có thể liên quan đến nhiều hơn một? Là một giá trị nhất định luôn luôn liên quan đến khác? Họ có chia sẻ cùng một dữ liệu phổ biến khác không?
Philᵀᴹ

Có, chúng có thể liên quan đến hơn 1 hàng khác. Không, chúng không nhất thiết luôn luôn liên quan đến một hàng khác. Họ không nhất thiết phải có bất kỳ dữ liệu chung nào. Cảm ơn bạn.
jpmc26

Giáo sư. Quên @Phil. Cũng được chỉnh sửa để thêm một cấu trúc tiềm năng vừa xảy ra với tôi.
jpmc26

Câu trả lời:


9

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"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 CHECKrà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_id1và id cao hơn trong x_id2cộ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
);
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.