Sql – postgres: How to prevent INSERT in a special case

databasepostgresqlsql

I got a table 'foo' that looks like

   ID  | NAME 
 ------+----------------------------
   123 | PiratesAreCool
   254 | NinjasAreCoolerThanPirates

and a second table 'bar'

  SID  |  ID  |  created   |  dropped
 ------+------+------------+-----------
  9871 |  123 | 03.24.2009 | 03.26.2009
  9872 |  123 | 04.02.2009 | 

bar.ID is a reference (foreign key) to foo.ID.

Now I want to prevent that you can insert a new record to 'bar' when there is a record with the same ID and bar.dropped is null on that record.

So, when the 'bar' looks like above

   INSERT INTO BAR VALUES ('9873','123','07.24.2009',NULL);

should be forbidden, but

   INSERT INTO BAR VALUES ('9873','254','07.24.2009',NULL);

should be allowed (because there is no 'open' bar-record for 'NinjasAreCoolerThanPirates').

How do i do that?
I hope my problem is clear and somebody can help me.

Best Answer

hmm, that should be enough to just create a unique index.

create unique index ix_open_bar on bar (id, dropped);

of course, that would also have the effect that you can not drop a bar twice per day (unless the dropped is a timestamp which would minimize the risk)

Actually, I noticed that Postgres have support for partial indexes:

 create unique index ix_open_bar on bar (id) where dropped is null;

Update: After some tests, the unique constraint is not enforced on null values, but the partial indexes will still work.

And if you don't want to use the partial indexes, this might work as well:

 create unique index ix_open_bar on bar(id, coalesce(dropped, 'NULL'));

However, when using coalesce, you need to have the same datatypes on them (so if dropped is a timestamp, you need to change 'NULL' to a timestamp value instead).