PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Restringir o relacionamento de chave estrangeira a linhas de subtipos relacionados

Simplifique a construção em MATCH SIMPLE comportamento das restrições fk


Se pelo menos uma coluna de restrição estrangeira de várias colunas com padrão MATCH SIMPLE comportamento é NULL , a restrição não é aplicada. Você pode construir isso para simplificar amplamente seu 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
);

É muito barato para armazenar algumas colunas NULL adicionais (para entidades principais):

BTW, por documentação:

Dados de demonstração:
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 (incluindo seus testes).

Alternativa com FK único


Outra opção seria inserir todas as combinações de (status_id, sub) no status tabela (só pode haver 2 por status_id ) e tem apenas uma única restrição 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.

Respostas relacionadas:

Manter todas as tabelas


Se você precisar de todas as quatro tabelas por algum motivo que não esteja na pergunta, considere esta solução detalhada para uma pergunta muito semelhante no dba.SE:

Herança


... pode ser outra opção para o que você descreve. Se você pode viver com algumas grandes limitações . Resposta relacionada: