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

Validación de unicidad en la base de datos cuando la validación tiene una condición en otra tabla

Desafortunadamente, no existe una solución tan simple y limpia como para su pregunta anterior .

Esto debería hacer el trabajo:

  • Agregue una bandera redundante is_published al Child mesa

    ALTER TABLE child ADD column is_published boolean NOT NULL;
    

    Hágalo DEFAULT FALSE o lo que tenga normalmente en las columnas principales al insertar.
    Debe ser NOT NULL para evitar una escapatoria con NULL valores y predeterminado MATCH SIMPLE comportamiento en claves foráneas:
    Restricción de clave externa de dos columnas solo cuando la tercera columna NO es NULL

  • Agregue una restricción única (aparentemente inútil, sin embargo) en parent(parent_id, is_published)

    ALTER TABLE parent ADD CONSTRAINT parent_fk_uni
    UNIQUE (parent_id, is_published);
    

    Desde parent_id es la clave principal, la combinación sería única de cualquier manera. Pero eso es necesario para la siguiente restricción fk.

  • En lugar de hacer referencia a parent(parent_id) con una simple restricción de clave externa , cree una clave externa de varias columnas en (parent_id, is_published) con ON UPDATE CASCADE .
    De esta manera, el estado de child.is_published es mantenido y aplicado por el sistema de forma automática y más confiable de lo que podría implementar con disparadores personalizados:

    ALTER TABLE child
    ADD CONSTRAINT child_special_fkey FOREIGN KEY (parent_id, is_published)
    REFERENCES parent (parent_id, is_published) ON UPDATE CASCADE;
    
  • Luego agregue un índice ÚNICO parcial como en tu respuesta anterior.

    CREATE UNIQUE INDEX child_txt_is_published_idx ON child (text)
    WHERE is_published;
    

Por supuesto, al insertar filas en el child estás obligado a usar el estado actual de parent.is_published ahora. Pero ese es el punto:hacer cumplir la integridad referencial.

Esquema completo

O, en lugar de adaptar un esquema existente, aquí está el diseño completo:

CREATE TABLE parent(
    parent_id serial PRIMARY KEY
  , is_published bool NOT NULL DEFAULT FALSE
--, more columns ...
  , UNIQUE (parent_id, is_published)   -- required for fk
);

CREATE TABLE child (
    child_id serial PRIMARY KEY
  , parent_id integer NOT NULL
  , is_published bool NOT NULL DEFAULT FALSE
  , txt text
  , FOREIGN KEY (parent_id, is_published)
      REFERENCES parent (parent_id, is_published) ON UPDATE CASCADE
);

CREATE UNIQUE INDEX child_txt_is_published_idx ON child (text)
WHERE is_published;