Thực thi các ràng buộc trong hai bàn cách xa nhau


10

Tôi gặp một số rắc rối khi mô hình hóa sơ đồ điện trong SQL. Cấu trúc tôi muốn nắm bắt là

  part ←────────── pin
                   
part_inst ←───── pin_inst

trong đó "inst" là viết tắt của "dụ".

Ví dụ: tôi có thể có một partop-amp pinLM58 với s 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT và V CC . Sau đó tôi có thể đặt phần này trên sơ đồ, tạo một part_instvà 8 pin_instgiây.

Bỏ qua các trường dữ liệu, nỗ lực ban đầu của tôi tại một lược đồ là

create table parts (
    part_id bigserial primary key
);
create table pins (
    pin_id bigserial primary key,
    part_id bigint not null references parts
);
create table part_insts (
    part_inst_id bigserial primary key,
    part_id bigint not null references parts
);
create table pin_insts (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null references part_insts,
    pin_id bigint not null references pins
);

Vấn đề chính với schema này là một pin_instcó thể được gắn với một part_instvới part_id=1nhưng nó pinpart_id=2.

Tôi muốn tránh vấn đề này ở cấp cơ sở dữ liệu hơn là cấp ứng dụng. Vì vậy, tôi đã sửa đổi các khóa chính của mình để thực thi điều đó. Tôi đánh dấu các dòng thay đổi với --.

create table parts (
    part_id bigserial primary key
);
create table pins (
    pin_id bigserial,                                          --
    part_id bigint not null references parts,
    primary key (pin_id, part_id)                              --
);
create table part_insts (
    part_inst_id bigserial,                                    --
    part_id bigint not null references parts,
    primary key (part_inst_id, part_id)                        --
);
create table pin_insts (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null,                              --
    pin_id bigint not null,                                    --
    part_id bigint not null references parts,                  --
    foreign key (part_inst_id, part_id) references part_insts, --
    foreign key (pin_id, part_id) references pins              --
);

Điều hấp dẫn của tôi với phương pháp này là nó làm ô nhiễm các khóa chính: Ở mọi nơi tôi đề cập đến part_inst, tôi cần theo dõi cả hai part_inst_idpart_id. Có cách nào khác để tôi có thể thực thi các ràng buộc pin_inst.part_inst.part_id = pin_inst.pin.part_idmà không quá dài dòng không?


Bạn cũng có thể loại bỏ pin_inst_idcái dư thừa. Bạn có thể sử dụng (part_inst_id, part_id, pin_id)khóa chính.
ypercubeᵀᴹ

Hai điều: (a) không 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT và VCC mang lại 11 trường hợp pin? (b) Tôi không nhận được lược đồ ban đầu của bạn. Không thể sử dụng pin trong nhiều phần? Bạn cần một mối quan hệ NN giữa pin và phần không phải là 1-N.
Marcus Junius Brutus

@ user34332: (a) Các số là một phần của tên. Ví dụ: "2OUT" là một pin đơn. Đây là bản vẽ sơ đồ của con chip mà tôi đang nói đến trong câu hỏi. (b) Tôi không đồng ý. Chắc chắn hai phần có thể có một chân VCC (điện áp cung cấp dương, "điện áp [tại] bộ thu chung"), nhưng chúng là các chân khác nhau về mặt logic. Ví dụ: một chân VCC thường có thể rút ra 500 PhaA và một pin 250 khác nhau.
Snowball

@Snowball Nó sẽ giúp người khác hiểu lược đồ của bạn nếu bạn đã thêm SQL-Fiddle với dữ liệu mẫu.
ypercubeᵀᴹ

Câu trả lời:


13

Giải pháp tối thiểu

Một giải pháp căn cơ có thể là loại bỏ pin_insthoàn toàn:

  part ←────────── pin
                   
part_inst ←───── pin_inst

Không có gì trong câu hỏi của bạn để đề nghị bạn thực sự cần bảng dự phòng. Đối với pins liên quan đến a part_inst, hãy nhìn vào pins của liên kết part.

Điều đó sẽ đơn giản hóa mã thành:

create table part (    -- using singular terms for table names
    part_id bigserial primary key
);
create table pin (
    pin_id bigserial primary key,
    part_id bigint not null references part
);
create table part_inst (
    part_inst_id bigserial primary key,
    part_id bigint not null references part
);

Nhưng bình luận của bạn nói rõ rằng chúng tôi sẽ không thoát khỏi điều đó ...

Thay thế nếu pin_instcần thiết

Bao gồm part_idnhư bạn đã làm là giải pháp đơn giản nhất với các ràng buộc khóa ngoại. Bạn không thể tham khảo một bảng Bảng cách xa hai bảng với các ràng buộc khóa ngoài .

Nhưng ít nhất bạn có thể thực hiện mà không "làm ô nhiễm" các khóa chính. Thêm các UNIQUEràng buộc .

create table part (
    part_id bigserial primary key
);
create table pin (
    pin_id bigserial primary key,
    part_id bigint not null references part,
    unique(part_id, pin_id)         -- note sequence of columns
);
create table part_inst (
    part_inst_id bigserial primary key,
    part_id bigint not null references part,
    unique(part_id, part_inst_id)
);
create table pin_inst (
    pin_inst_id bigserial primary key,
    part_inst_id bigint not null,
    pin_id bigint not null,
    part_id bigint not,
    foreign key (part_id, pin_id) references pin,
    foreign key (part_id, part_inst_id) references part_inst
);

Tôi đặt part_idđầu tiên trong các ràng buộc độc đáo. Điều đó không liên quan đến tính toàn vẹn tham chiếu, nhưng nó quan trọng đối với hiệu suất. Các khóa chính đã thực hiện các chỉ mục cho các cột pk. Tốt hơn là nên có cột khác đầu tiên trong các chỉ mục nhiều màu thực hiện các ràng buộc duy nhất. Chi tiết dưới những câu hỏi liên quan:

Các câu hỏi liên quan về SO:

Thay thế bằng kích hoạt

Bạn có thể sử dụng các hàm kích hoạt, linh hoạt hơn, nhưng phức tạp hơn một chút và dễ bị lỗi và ít nghiêm ngặt hơn một chút. Lợi ích: bạn có thể làm mà không cần part_inst.part_idpin.part_id...


Có một số cột bổ sung pin_insts, nhưng tôi đã bỏ qua chúng vì lợi ích của khả năng đọc ("Bỏ qua các trường dữ liệu, [...]"). Ví dụ, a pin_instcó thể được đánh dấu là đầu vào hoặc đầu ra.
Snowball

@Snowball: Rất dễ trở thành sự thật. Tôi mở rộng một chút về giải pháp của bạn.
Erwin Brandstetter

2
Đề nghị thứ hai của bạn hoạt động tốt cho tình hình của tôi. Tôi không biết rằng khóa ngoại có thể tham chiếu thứ gì đó ngoài khóa chính.
Snowball
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.