I have a shopping list application where users can drag and drop an arbitrary number of items into a list. Items are organized in two-dimensional order, where each one has an integer position, i.e. indexes. As is common with this domain, no two items can have the same position.
This is currently represented in the following table:
id list_id name position
------------------------------------
1 1 apple 0
2 1 banana 1
3 1 orange 2
4 2 milk 3
list_id
is a foreign key to another table (lists
).
This table has the following unique constraint:
ALTER TABLE public.items
ADD CONSTRAINT unique_position
UNIQUE (list_id, position)
DEFERRABLE INITIALLY IMMEDIATE;
This helps maintain data integrity, and prevents scenarios where a bug in the application (or a naughty user) causes two items to be inserted in the exact same position. (The reason it is DEFERRABLE
is that the constraint needs to be deferred until the end of a transaction containing multiple UPDATE position
statements.)
Now I want to add a feature whereby the user can also create sub-lists inside the main list and insert the items in those sub-lists, similarly in a sequential fashion.
My plan is to add a sublist_id
column to the original items
table:
id list_id name position sublist_id
-----------------------------------------------------
1 1 apple 0 NULL
2 1 banana 1 NULL
3 1 orange 2 NULL
4 2 milk 0 NULL
5 2 chicken 1 NULL
6 2 eggs 2 NULL
7 2 coke NULL 1
8 2 water NULL 1
And have that reference a second table called sublists
:
id list_id name position_in_list
------------------------------------------
1 2 drinks 3
And then connect the two tables using a third table called sublist_items
:
id sublist_id item_id position
----------------------------------------
1 1 7 0
1 1 8 1
The issue with this is that the items
table will list some item positions as NULL, since those items are now inside a sublist, and it is sufficient to keep track of the position of the sublist inside the main list, and the positions of the items inside the sublist.
QUESTION 1: How can the original constraint be changed such that it is applied across two tables? In other words, I still want each item in the list (whether it is an item
or a sublist
) to occupy a unique position, except the position
values coming from both items
and sublist_items
tables, with list_id
being the commonality.
QUESTION 2: Is there a better way to organize the data? I know I technically don't need the sublist_id
column in the first table, but I figured I would add it so that it would allow me to write additional constraints, e.g. "allow position
to be null only if sublist_id
is not null".