A composite unique key makes the combined values of several columns identify a row combination. In an inventory table, (product_id, warehouse_id, area) distinguishes stock records and lets an upsert update a matching row instead of inserting another. section 2.11
The NULL upsert trap
A unique index on (product_id, warehouse_id, area) behaves as expected for area = 'freezer': a second upsert updates quantity from 10 to 20. With area = NULL, PostgreSQL treats the repeated composite values as distinct, so quantities 5 and 7 become separate rows instead of one row being updated. Avoid nullable columns in composite unique keys when possible. section 2.8
PostgreSQL 15 and later can change this behavior with NULLS NOT DISTINCT. Recreate the unique index with that clause so matching NULL values participate in conflict detection and the upsert updates the existing row. section 2.8







