Durchsetzen von Einschränkungen "zwei Tabellen entfernt"

9

Ich hatte Probleme beim Modellieren eines elektrischen Schaltplans in SQL. Die Struktur, die ich erfassen möchte, ist

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

Dabei steht "inst" für "instance".

Zum Beispiel könnte ich als partLM358 Operationsverstärker mit pins 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT und V CC haben . Ich könnte diesen Teil dann auf einen Schaltplan setzen und a part_instund 8 pin_insts erstellen .

Ich ignorierte Datenfelder und versuchte zunächst, ein Schema zu erstellen

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
);

Das Hauptproblem bei diesem Schema ist, dass a pin_instmöglicherweise an a part_instmit gebunden ist, part_id=1aber es pinhat part_id=2.

Ich möchte dieses Problem eher auf Datenbankebene als auf Anwendungsebene vermeiden. Also habe ich meine Primärschlüssel geändert, um dies durchzusetzen. Ich habe die geänderten Zeilen mit markiert --.

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              --
);

Mein Problem mit dieser Methode ist, dass sie die Primärschlüssel verschmutzt: Überall, wo ich auf a verweise, part_instmuss ich sowohl das part_inst_idals auch das verfolgen part_id. Gibt es eine andere Möglichkeit, die Einschränkung durchzusetzen, pin_inst.part_inst.part_id = pin_inst.pin.part_idohne übermäßig ausführlich zu sein?

Schneeball
quelle
Sie können auch die pin_inst_idRedundanz entfernen . Sie können den (part_inst_id, part_id, pin_id)als Primärschlüssel verwenden.
Ypercubeᵀᴹ
Zwei Dinge: (a) Ergibt 1OUT, 1IN-, 1IN +, GND, 2IN +, 2IN-, 2OUT und VCC keine 11-Pin-Instanzen? (b) Ich verstehe Ihr ursprüngliches Schema nicht. Kann ein Stift nicht in mehr als einem Teil verwendet werden? Sie benötigen eine NN-Beziehung zwischen Pin und Teil, keine 1-N.
Marcus Junius Brutus
@ user34332: (a) Die Nummern sind Teil der Namen. Zum Beispiel ist "2OUT" ein einzelner Pin. Hier ist eine schematische Zeichnung des Chips, über den ich in der Frage spreche. (b) Ich bin anderer Meinung. Sicherlich haben zwei Teile einen VCC-Pin (positive Versorgungsspannung, "Spannung [am] gemeinsamen Kollektor"), aber sie sind logisch unterschiedliche Pins. Beispielsweise kann ein VCC-Pin typischerweise 500 µA und ein anderer 250 µA ziehen.
Schneeball
@Snowball Es würde anderen helfen, Ihr Schema zu verstehen, wenn Sie eine SQL-Geige mit Beispieldaten hinzufügen .
Ypercubeᵀᴹ
1
Eng verwandte Frage.
Erwin Brandstetter

Antworten:

12

Minimale Lösung

Eine radikale Lösung könnte darin bestehen, sie pin_instvollständig zu entfernen :

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

Ihre Frage enthält nichts, was darauf hindeutet, dass Sie die redundante Tabelle tatsächlich benötigen. Schauen Sie sich für pins, die mit a verknüpft sind part_inst, das pins des zugeordneten s an part.

Das würde den Code vereinfachen, um:

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
);

Aber Ihr Kommentar hat deutlich gemacht, dass wir damit nicht durchkommen werden ...

Alternative, wenn pin_instnötig

Einschließen part_idwie Sie ist die einfachste Lösung mit Fremdschlüsseleinschränkungen. Sie können nicht auf eine Tabelle "zwei Tabellen entfernt" mit Fremdschlüsseleinschränkungen verweisen .

Aber Sie können zumindest auskommen, ohne die Primärschlüssel zu "verschmutzen". Fügen Sie UNIQUEEinschränkungen hinzu .

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
);

Ich stelle part_idzuerst die einzigartigen Einschränkungen ein. Das ist für die referenzielle Integrität irrelevant, aber für die Leistung von Bedeutung. Die Primärschlüssel implementieren bereits Indizes für die pk-Spalten. Es ist besser, die andere Spalte zuerst in den mehrspaltigen Indizes zu haben, um die eindeutigen Einschränkungen zu implementieren. Details unter diesen verwandten Fragen:

Verwandte Fragen zu SO:

Alternative mit Triggern

Sie könnten auf Triggerfunktionen zurückgreifen, die flexibler, aber etwas komplizierter und fehleranfälliger und etwas weniger streng sind. Der Vorteil: Sie könnten ohne part_inst.part_idund pin.part_id...

Erwin Brandstetter
quelle
Es gibt einige zusätzliche Spalten pin_insts, die ich jedoch aus Gründen der Lesbarkeit weggelassen habe ("Datenfelder ignorieren, [...]"). Beispielsweise kann a pin_instals Eingabe oder Ausgabe markiert werden.
Schneeball
@Snowball: Wäre zu einfach gewesen, um wahr zu sein. Ich habe Ihre Lösung etwas erweitert.
Erwin Brandstetter
1
Ihr zweiter Vorschlag passt gut zu meiner Situation. Mir war nicht bewusst, dass ein Fremdschlüssel auf etwas anderes als den Primärschlüssel verweisen könnte.
Schneeball