1

I am wondering why Postgres allows to insert a non-existing values for a foreign key to a nullable multicolumn unique constraint. (Cannot be an actual primary key.)

I made a fiddle. I expected this to fail:

INSERT INTO c (intermediate_item_id, intermediate_extra_item_id) VALUES (5,null);

I don't understand why it is possible. Doesn't it break the concept of relational databases?

5
  • 1
    In any check (CHECK or FOREING KEY constraint) only FALSE causes an error whereas TRUE or NULL allows the operation. This is "by design" behaviour. Commented Aug 27 at 16:24
  • 4
    This question is similar to: MATCH FULL vs MATCH SIMPLE in foreign key constraints. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Aug 27 at 16:42
  • 1
    MATCH SIMPLE is the default, which allows this. You want MATCH FULL. See eg dbfiddle.uk/vsN_MLZb Commented Aug 27 at 16:53
  • For this data model, you would need MATCH PARTIAL. However, MATCH PARTIAL is not yet implemented. (Of course, NOT NULL constraints can be applied to the referencing column(s) to prevent these cases from arising.) (version 18, currently in Beta) Commented Aug 27 at 17:02
  • NULL is not a value. NULL in a FK column is a common way of modeling an optional relationship. Commented Sep 27 at 11:46

1 Answer 1

3

With default MATCH SIMPLE behavior, a referencing row need not satisfy the foreign key constraint if any of its referencing columns are null. The (non-default) MATCH FULL behavior enforces your desired behavior. See:

Also, your whole setup can be radically simplified with the NULLS NOT DISTINCT clause for the UNIQUE constraint since Postgres 15. See:

So:

CREATE TABLE p (
  item_id int NOT NULL
, extra_item_id int
, UNIQUE NULLS NOT DISTINCT (item_id, extra_item_id)  -- !
);

CREATE TABLE c (
  intermediate_item_id int
, intermediate_extra_item_id int
, FOREIGN KEY (intermediate_item_id, intermediate_extra_item_id)
    REFERENCES p (item_id, extra_item_id) MATCH FULL  -- !
);

fiddle

Wouldn't work for an actual PRIMARY KEY (instead of the UNIQUE constraint) like you formulated a bit loosely at first, since PK columns are NOT NULL implicitly. See:

2
  • I understand, but this does not allow to insert c(1,null) even if p(1,null) does exist. Right? I fear the only way is to use a trigger. Commented Aug 28 at 6:53
  • 1
    Or use an actial value instead of null. Commented Aug 28 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.