Differences between MATCH FULL, MATCH SIMPLE, and MATCH PARTIAL?

Check the CREATE TABLE page of the manual:

There are three match types: MATCH FULL, MATCH PARTIAL, and MATCH SIMPLE (which is the default). MATCH FULL will not allow one column of a multicolumn foreign key to be null unless all foreign key columns are null; if they are all null, the row is not required to have a match in the referenced table. MATCH SIMPLE allows any of the foreign key columns to be null; if any of them are null, the row is not required to have a match in the referenced table. 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.)

Also, in the chapter on Foreign Keys:

Normally, a referencing row need not satisfy the foreign key constraint if any of its referencing columns are null. If MATCH FULL is added to the foreign key declaration, a referencing row escapes satisfying the constraint only if all its referencing columns are null (so a mix of null and non-null values is guaranteed to fail a MATCH FULL constraint). If you don't want referencing rows to be able to avoid satisfying the foreign key constraint, declare the referencing column(s) as NOT NULL.

And be sure to consult the current manual or the version matching your installation. Don't fall for outdated Google links to outdated versions.


FULL vs SIMPLE vs PARTIAL

While the chosen answer is correct, if this is new to you, you may want to see it with code -- I think it's easier to grok that way.

-- one row with (1,1)
CREATE TABLE foo ( a int, b int,
  PRIMARY KEY (a,b)
);
INSERT INTO foo (a,b) VALUES (1,1);

--
-- two child tables to reference it
-- 
CREATE TABLE t_full ( a int, b int,
  FOREIGN KEY (a,b) REFERENCES foo MATCH FULL
);
CREATE TABLE t_simple ( a int, b int,
  FOREIGN KEY (a,b) REFERENCES foo MATCH SIMPLE
);

Logically, with FULL and SIMPLE, we can insert a full match.

-- works
INSERT INTO t_full (a,b) VALUES (1,1);
INSERT INTO t_simple (a,b) VALUES (1,1);

The problem comes when one of the columns is NULL.

-- works
INSERT INTO t_simple (a,b) VALUES (1,NULL);

-- fails
INSERT INTO t_full (a,b) VALUES (1,NULL);

The insert into t_full generates the following error,

ERROR:  insert or update on table "t_full" violates foreign key constraint "t_full_a_fkey"
DETAIL:  MATCH FULL does not allow mixing of null and nonnull key values.
INSERT 0 1

Ok, so what about (42,NULL) -- this is the part that I always found confusing about MATCH SIMPLE,

-- works
INSERT INTO t_simple (a,b) VALUES (42,NULL);

The above behavior would NOT work with the unimplemented MATCH PARTIAL, which likely does what you want for a compound index where the right-most column are NULLed out. However, some people view that as a method of opening up a Pandora's box to bad design.

Simple Definitions & Mnemonics

  • MATCH FULL everything must fully match, or all columns must be NULL
  • MATCH SIMPLE if one thing is NULL the constraint is simply ignored.
  • MATCH PARTIAL if one thing is NULL the fact that not everything is NULL is partially salvaged by doing something sensible for the purpose of the constraint.

SQL Spec Notes

For posterity, here are the definitions from the SQL Spec on the <match type>

  • MATCH SIMPLE if at least one referencing column is null, then the row of the referencing table passes the constraint check. If all referencing columns are not null, then the row passes the constraint check if and only if there is a row of the referenced table that matches all the referencing columns.
  • MATCH PARTIAL: if all referencing columns are null, then the row of the referencing table passes the constraint check. If at least one referencing columns is not null, then the row passes the constraint check if and only if there is a row of the referenced table that matches all the non-null referencing columns.
  • MATCH FULL: if all referencing columns are null, then the row of the referencing table passes the constraint check. If all referencing columns are not null, then the row passes the constraint check if and only if there is a row of the referenced table that matches all the referencing columns. If some referencing column is null and another referencing column is non-null, then the row of the referencing table violates the constraint check.

While this is not PostgreSQL specific these examples are demonstrated with PostgreSQL