Uniqueness validation in database when validation has a condition on another table

只愿长相守 提交于 2019-12-05 14:52:35
Erwin Brandstetter

Unfortunately, there is no solution quite as simple and clean as for your previous question.

This should do the job:

  • Add a redundant flag is_published to the Child table

    ALTER TABLE child ADD column is_published boolean NOT NULL;
    

    Make it DEFAULT FALSE or whatever you typically have in parent columns when inserting.
    It needs to be NOT NULL to avoid a loophole with NULL values and default MATCH SIMPLE behaviour in foreign keys:
    Two-column foreign key constraint only when third column is NOT NULL

  • Add a (seemingly pointless, yet) unique constraint on parent(parent_id, is_published)

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

    Since parent_id is the primary key, the combination would be unique either way. But that's required for the following fk constraint.

  • Instead of referencing parent(parent_id) with a simple foreign key constraint, create a multi-column foreign key on (parent_id, is_published) with ON UPDATE CASCADE.
    This way, the state of child.is_published is maintained and enforced by the system automatically and more reliably than you could implement with custom triggers:

    ALTER TABLE child
    ADD CONSTRAINT child_special_fkey FOREIGN KEY (parent_id, is_published)
    REFERENCES parent (parent_id, is_published) ON UPDATE CASCADE;
    
  • Then add a partial UNIQUE index like in your previous answer.

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

Of course, when inserting rows in the child table you are forced to use the current state of parent.is_published now. But that's the point: to enforce referential integrity.

Complete schema

Or, instead of adapting an existing schema, here is the complete layout:

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;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!