What is the most efficient way to create a many to many (N:M) relation? In my example I am fighting a polymorphic issue too. But I think my question works for any N:M relation.
- Should I create a unique_index()?
- Should I keep the timestamps()?
- Is it a bad idea to add on_delete:?
I’m using MySQL/MariaDB. I don’t fully understand exactly how references() works in memory. (I don’t know if the DB will use the b-tree from the key with both items, or the reference b-tree, or what)
This is modified from the example found in: https://hexdocs.pm/ecto/polymorphic-associations-with-many-to-many.html
Original:
create table(:todo_list_items) do
add :todo_item_id, references(:todo_items)
add :todo_list_id, references(:todo_lists)
timestamps()
end
create table(:project_items) do
add :todo_item_id, references(:todo_items)
add :project_id, references(:projects)
timestamps()
end
What’s wrong with:
create table(:todo_list_items) do
add :todo_item_id, references(:todo_items, on_delete: :delete_all)
add :todo_list_id, references(:todo_lists, on_delete: :delete_all)
create unique_index(:todo_list_items, [:todo_item_id, :todo_list_id])
end
create table(:project_items) do
add :todo_item_id, references(:todo_items, on_delete: :delete_all)
add :project_id, references(:projects, on_delete: :delete_all)
create unique_index(:todo_list_items, [:todo_item_id, :project_id])
end
1. Should I create a unique_index()?
Back in the good 'ol days I would create a unique index with both items in the join table. A) So both the items were in memory meaning the DB could find the associated object IDs without having to touch disk. B) The unique part made sure my DB enforced the sanity despite my bad programming.
If I use references, do I lose the advantage of having the key IDs next to each other in memory? Should I keep it just to prevent duplicate pairs of IDs?
2. Should I keep the timestamps()?
If I don’t care when the relation was created, can I ditch this and save on space, and increase DB speed?
3. Is it a bad idea to add on_delete:?
If the item on the left (todo_list) or the item on the right (item) get deleted, I want to ensure the n:m table cleans it self up. So wouldn’t it be good to include this?
BONUS Question: My n:m join table is to handle the nasty polymorphic situation I’m stuck in. In truth this table is just a 1:1 map between tables. If the left side is deleted, or the right side is deleted, I don’t just want to delete the join table item. I want to delete everything on the left and right. (If the user is deleted, I want everything to go. I want projects to delete all the associated items. I want todo to then delete all the associated items via todos. ) I can hack it in the code, but it would be wicked awesome if the DB would would enforce this like it does with references().