PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Beschränken Sie die Fremdschlüsselbeziehung auf Zeilen verwandter Untertypen

Vereinfachen Sie den Aufbau auf MATCH SIMPLE Verhalten von fk-Einschränkungen

Wenn mindestens eine Spalte einer mehrspaltigen Fremdbeschränkung mit dem Standardwert MATCH SIMPLE Verhalten ist NULL , wird die Einschränkung nicht erzwungen. Darauf können Sie aufbauen, um Ihr Design weitgehend zu vereinfachen.

CREATE SCHEMA test;

CREATE TABLE test.status(
   status_id  integer PRIMARY KEY
  ,sub        bool NOT NULL DEFAULT FALSE  -- TRUE .. *can* be sub-status
  ,UNIQUE (sub, status_id)
);

CREATE TABLE test.entity(
   entity_id  integer PRIMARY KEY
  ,status_id  integer REFERENCES test.status  -- can reference all statuses
  ,sub        bool      -- see examples below
  ,additional_col1 text -- should be NULL for main entities
  ,additional_col2 text -- should be NULL for main entities
  ,FOREIGN KEY (sub, status_id) REFERENCES test.status(sub, status_id)
     MATCH SIMPLE ON UPDATE CASCADE  -- optionally enforce sub-status
);

Es ist sehr billig um einige zusätzliche NULL-Spalten zu speichern (für Hauptentitäten):

Übrigens, pro Dokumentation:

Demo-Daten:

INSERT INTO test.status VALUES
  (1, TRUE)
, (2, TRUE)
, (3, FALSE);     -- not valid for sub-entities

INSERT INTO test.entity(entity_id, status_id, sub) VALUES
  (11, 1, TRUE)   -- sub-entity (can be main, UPDATES to status.sub cascaded)
, (13, 3, FALSE)  -- entity  (cannot be sub,  UPDATES to status.sub cascaded)
, (14, 2, NULL)   -- entity  (can    be sub,  UPDATES to status.sub NOT cascaded)
, (15, 3, NULL)   -- entity  (cannot be sub,  UPDATES to status.sub NOT cascaded)

SQL-Fiddle (einschließlich Ihrer Tests).

Alternative mit einfachem FK

Eine andere Möglichkeit wäre, alle Kombinationen von (status_id, sub) einzugeben in den status Tabelle (es kann nur 2 pro status_id geben ) und haben nur eine einzige fk-Einschränkung:

CREATE TABLE test.status(
   status_id  integer
  ,sub        bool DEFAULT FALSE
  ,PRIMARY KEY (status_id, sub)
);

CREATE TABLE test.entity(
   entity_id  integer PRIMARY KEY
  ,status_id  integer NOT NULL  -- cannot be NULL in this case
  ,sub        bool NOT NULL     -- cannot be NULL in this case
  ,additional_col1 text
  ,additional_col2 text
  ,FOREIGN KEY (status_id, sub) REFERENCES test.status
     MATCH SIMPLE ON UPDATE CASCADE  -- optionally enforce sub-status
);

INSERT INTO test.status VALUES
  (1, TRUE)       -- can be sub ...
  (1, FALSE)      -- ... and main
, (2, TRUE)
, (2, FALSE)
, (3, FALSE);     -- only main

usw.

Verwandte Antworten:

Alle Tabellen behalten

Wenn Sie aus irgendeinem Grund alle vier Tabellen benötigen, die nicht in der Frage enthalten sind, ziehen Sie diese detaillierte Lösung für eine sehr ähnliche Frage zu dba.SE in Betracht:

Vererbung

... könnte eine andere Option für das sein, was Sie beschreiben. Wenn Sie mit einigen großen Einschränkungen . Zugehörige Antwort: