Discussion about this post

User's avatar
Barry Johnson's avatar

A couple of thoughts, overlapping other comments somewhat:

1. CROSS-TABLE CONSTRAINTS NOT WIDELY SUPPORTED

You want an entry in either 'coffee_free_days' or 'coffees' for a given day, but not both. Enforcing that as part of the schema definition requires a CONSTRAINT across both tables. Alas, the only cross-table CONSTRAINT generally-supported is a FOREIGN KEY, which can't do what you're seeking.

2. TRYING TO IMPLEMENT A SUBTYPE

In this case, you have an entity of 'day', and you want 3 specializations of it:

- implicitly no coffee drunk: nothing recorded for the day.

- explicitly no coffee drunk: "coffee-free day" button clicked.

- one or more 'coffees' recorded for the day.

Relational model doesn't support the concept of subtype. We might get something close in this case with:

CREATE TABLE days(

day DATE NOT NULL PRIMARY KEY ) ;

CREATE TABLE coffees(

id INTEGER NOT NULL PRIMARY KEY,

day DATE NOT NULL REFERENCES days( day ),

time TIME NOT NULL,

what VARCHAR( 32 ) NOT NULL,

price DECIMAL( 5,2 ) NOT NULL ) ;

This changes a couple of semantics:

- 'days' records days when either "coffee-free day" was pressed *or* the first linked "coffees" entry for the day was entered, and

- a 'days' entry *without* linked 'coffees' must have been recorded as a "coffee-free day".

This would required the code to check for the following errors:

- "coffee-free day" is pressed when a "days" entry already exists linked to a "coffees" entry

- a coffee is entered but a "days" entry already exists *without* a linked "coffees" entry implying it was previously recorded as a "coffee-free day".

Actually, I have many more thoughts, but that would be a much longer conversation.

Expand full comment
Jakub Zárybnický's avatar

I think this would be implementable using a single table (status_updates) using PostgreSQL exclusion constraints - not sure if you would consider them "trigger-like" or not.

Hopefully this comes through with Substack's formatting; the hash is postgres xor (using btree_gist or gin?), the meaning should be "on a single day, you can only have rows which all have equal is_coffee_free flag (all true or all false)".

`ALTER TABLE status_updates ADD CONSTRAINT either_coffee_or_coffee_free EXCLUDE USING gist (date WITH =, is_coffee_free WITH #);`

You can augment this using a second partial unique index, `CREATE UNIQUE INDEX ON status_updates (date) WHERE (is_coffee_free);`, to ensure there can only ever be a single "coffee-free" check-in.

Haven't tried this specific exclusion constraint, but I've modelled similar problem shapes in a similar way, so it should work?

Edit: Coffee details could either be a nullable FK pointing to 'coffee_details', or a set of nullable columns enforced to be filled out using a similar type of a constraint (`is_coffee_free or (what is not null and price is not null)`)

Expand full comment
10 more comments...

No posts