Discussion about this post

User's avatar
Aleksey Zhidkov's avatar

Hello! I hope it’s not too late to chime in — apologies for the necroposting, I understand that this challenge is already almost a year old, but I couldn’t find any published results or a summary of submitted solutions. If there were any conclusions, I’d really appreciate a pointer.

In the proposed solution I use a custom type (without collections), which you consider acceptable for 1NF (if I understood Making sense of 1NF, pt. II correctly). It can also be replaced with plain columns if needed — we would lose some elegance, but not reliability.

Based on that interpretation, I believe there is a clean relational solution that:

* enforces all invariants,

* stays within 1NF as you describe it,

* avoids JSON, arrays of records, and triggers,

* and prevents all contradictory states.

Key ideas

A “coffee day” must contain at least one coffee entry.

Since enforcing that via a child table would require triggers, we instead store the first coffee directly in the parent table as a composite (scalar) coffee value.

Additional coffees (zero or more) may be stored in a child table.

This ensures that a “coffee day with an empty list” is structurally impossible.

And to prevent coffee records from appearing for non-coffee days, we include the day type in the parent table’s unique key, that is used as fk in coffee-entries table and add a check constraint type = 'coffee' in the table.

To prevent multiple records with different types for the same day, we also add a UNIQUE constraint on the day column in the parent table.

Why this satisfies the challenge

* Exactly one of the three states per day. day is unique; type is functionally dependent.

* No contradictory combinations across tables. Foreign keys + CHECK constraints ensure state coherence.

* A coffee day must contain ≥1 coffee. Enforced by the composite-typed coffee column being non-

* Coffee-free / avoid days cannot have coffees. Both the parent and child tables forbid this.

* Composite types are 1NF-safe - Per “pt. II”, they are fixed-arity atomic values, not nested relations.

* No JSON, no arrays-of-records, no triggers

Here is db-fiddle with propoed schema: https://www.db-fiddle.com/f/6bksKju2M8UJYJ4m96NoRu/0

Expand full comment
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
12 more comments...

No posts

Ready for more?