The whole purpose of a FOREIGN KEY
constraint is to disallow rows in table B that would violate the constraint - and be sure of it.
That said, you can make use of the default MATCH SIMPLE
behavior of multicolumn FK constraints for an elegant solution: FK is not enforced if at least one included column is NULL
. See:
You need an additional column, the boolean flag valid
in my demo:
CREATE TABLE project (
project_id int NOT NULL GENERATED ALWAYS AS IDENTITY
, project text NOT NULL
, project_id_valid boolean NOT NULL DEFAULT true -- NOT NULL!
, PRIMARY KEY(project_id, project_id_valid) -- !
, CONSTRAINT project_id_always_valid CHECK (project_id_valid) -- only true
);
COMMENT ON COLUMN project.project_id_valid IS 'Redundant column to allow conditional multicolumn FK reference in table organization';
INSERT INTO project(project) VALUES ('project1');
CREATE TABLE organization (
org_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, org text NOT NULL
, project_id integer NOT NULL
, project_id_valid boolean DEFAULT true -- can be NULL!
, CONSTRAINT valid_project_fk FOREIGN KEY (project_id, project_id_valid) REFERENCES project -- !
, CONSTRAINT project_id_valid_or_null CHECK (project_id_valid) -- only true
);
COMMENT ON COLUMN project.project_id_valid IS 'TRUE enforces the FK constraint valid_project_fk.';
db<>fiddle here
The model is simplified. Actual organizations would have multiple projects and you would use a many-to-many implementation. See:
CONSTRAINT project_id_always_valid CHECK (valid)
in combination with NOT NULL
on the column enforces project.project_id_valid
to always be true
, which makes it a noise column. We need it for the multicolumn PK to allow our multicolumn FK constraint in organization
. (Alternatively, use a plain PK on (project_id)
and add a multicolumn UNIQUE
constraint on (project_id, project_id_valid)
.)
The corresponding column organization.valid
can be true
or NULL
. If it's true
, the FK constraint is enforced, else it's not. Effectively, you can switch the FK on or off per row. Exactly what you are after.
These are standard SQL features. Still, be sure to document the purpose of the trickery for the afterworld. I added comments on the column project_id_valid
in this spirit.