PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Limita la relazione di chiave esterna alle righe di sottotipi correlati

Semplifica la creazione su MATCH SIMPLE comportamento dei vincoli fk

Se almeno una colonna di vincolo esterno multicolonna con MATCH SIMPLE predefinito il comportamento è NULL , il vincolo non viene applicato. Puoi basarti su questo per semplificare ampiamente il tuo design.

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

È molto economico per memorizzare alcune colonne NULL aggiuntive (per le entità principali):

A proposito, per documentazione:

Dati demo:

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 (compresi i tuoi test).

Alternativa con FK singolo

Un'altra opzione sarebbe quella di inserire tutte le combinazioni di (status_id, sub) nello status tabella (possono essercene solo 2 per status_id ) e hanno un solo vincolo fk:

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

ecc.

Risposte correlate:

Mantieni tutte le tabelle

Se hai bisogno di tutte e quattro le tabelle per qualche motivo non presente nella domanda, considera questa soluzione dettagliata a una domanda molto simile su dba.SE:

Ereditarietà

... potrebbe essere un'altra opzione per ciò che descrivi. Se riesci a convivere con alcune importanti limitazioni . Risposta correlata: