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

Convalida dell'unicità nel database quando la convalida ha una condizione su un'altra tabella

Sfortunatamente, non esiste una soluzione così semplice e pulita come per la tua domanda precedente .

Questo dovrebbe fare il lavoro:

  • Aggiungi un flag ridondante is_published al Child tabella

    ALTER TABLE child ADD column is_published boolean NOT NULL;
    

    Rendilo DEFAULT FALSE o qualunque cosa tu abbia in genere nelle colonne principali durante l'inserimento.
    Deve essere NOT NULL per evitare una scappatoia con NULL valori e predefinito MATCH SIMPLE comportamento nelle chiavi esterne:
    vincolo di chiave esterna a due colonne solo quando la terza colonna NON è NULL

  • Aggiungi un vincolo univoco (apparentemente inutile, ancora) su parent(parent_id, is_published)

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

    Da parent_id è la chiave primaria, la combinazione sarebbe univoca in entrambi i casi. Ma è necessario per il seguente vincolo fk.

  • Invece di fare riferimento a parent(parent_id) con un semplice vincolo di chiave esterna , crea una chiave esterna a più colonne su (parent_id, is_published) con ON UPDATE CASCADE .
    In questo modo, lo stato di child.is_published viene mantenuto e applicato dal sistema automaticamente e in modo più affidabile di quanto potresti implementare con trigger personalizzati:

    ALTER TABLE child
    ADD CONSTRAINT child_special_fkey FOREIGN KEY (parent_id, is_published)
    REFERENCES parent (parent_id, is_published) ON UPDATE CASCADE;
    
  • Quindi aggiungi un indice UNIQUE parziale come nella tua risposta precedente.

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

Naturalmente, quando si inseriscono righe nel child tabella sei costretto a usare lo stato corrente di parent.is_published adesso. Ma questo è il punto:imporre l'integrità referenziale.

Schema completo

Oppure, invece di adattare uno schema esistente, ecco il layout 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;