sql >> Base de Datos >  >> RDS >> PostgreSQL

Restringir la relación de clave externa a filas de subtipos relacionados

Simplifique la construcción en MATCH SIMPLE comportamiento de las restricciones fk

Si al menos una columna de restricción foránea multicolumna con MATCH SIMPLE predeterminado el comportamiento es NULL , la restricción no se aplica. Puede basarse en eso para simplificar en gran medida su diseño.

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 muy barato para almacenar algunas columnas NULL adicionales (para entidades principales):

Por cierto, según documentación:

Datos de demostración:

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 (incluidas sus pruebas).

Alternativa con solo FK

Otra opción sería ingresar todas las combinaciones de (status_id, sub) en el status tabla (solo puede haber 2 por status_id ) y solo tienen una sola restricción 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

Etc.

Respuestas relacionadas:

Mantener todas las mesas

Si necesita las cuatro tablas por algún motivo que no está en la pregunta, considere esta solución detallada para una pregunta muy similar en dba.SE:

Herencia

... podría ser otra opción para lo que describes. Si puede vivir con algunas limitaciones importantes . Respuesta relacionada: