views:

72

answers:

6

So imagine you have multiple tables in your database each with it's own structure and each with a PRIMARY KEY of it's own.

Now you want to have a Favorites table so that users can add items as favorites. Since there are multiple tables the first thing that comes in mind is to create one Favorites table per table:

Say you have a table called Posts with PRIMARY KEY (post_id) and you create a Post_Favorites with PRIMARY KEY (user_id, post_id)

This would probably be the simplest solution, but could it be possible to have one Favorites table joining across multiple tables?

I've though of the following as a possible solution:

Create a new table called Master with primary key (master_id). Add triggers on all tables in your database on insert, to generate a new master_id and write it along the row in your table. Also let's consider that we also write in the Master table, where the master_id has been used (on which table)

Now you can have one Favorites table with PRIMARY KEY (user_id, master_id)

You can select the Favorites table and join with each individual table on the master_id and get the the favorites per table. But would it be possible to get all the favorites with one query (maybe not a query, but a stored procedure?)

Do you think that this is a stupid approach? Since you will perform one query per table what are you gaining by having a single table?

What are your thoughts on the matter?

A: 

An alternative to your approach might be to have the favorites table as user_id, object_id, object_type. When inserting in the favorites table just insert the type of the favorite. However i dont see a simple query being able to work with your approach or mine. One way to go about it might be to use UNION and get one combined resultset and then identify what type of record it is based on the type. Another thing you can do is, turn the UNION query into a MySQL VIEW and simply query that VIEW.

The benefit of using a single table for favorites is a simplicity, which some might consider as against the database normalization rules. But on the upside, you dont have to create so many favorites table and you can add anything to favorites easily by just coming up with a new object_type identifier.

Sabeen Malik
MySQL Views are so primitive (no index on views) that perhaps using them in the way I have in mind could be a blocking in a production environment...
mobius
I just suggested views for convenience, maybe it would just be better to use the raw query itself.
Sabeen Malik
A: 

I'm not sure I really understand the alternative you propose.

But in general, when given the choice of 1) "more tables" or 2) "a mega-table supported by a bunch of fancy code work" ..your interests are best served by more tables without the code gymnastics.

A Red Flag was "Add triggers on all tables in your database" each trigger fire is a performance hit of it's own.

The database designers have built in all kinds of technology to optimize tables/indexes, much of it behind the scenes without you knowing it. Just sit back and enjoy the ride.

Try these for inspiration Database Answers ..no affiliation to me.

Paulb
You are definitely right on the triggers but I believe that this is not such an issue. I don't expect too much load coming from there. One the other hand what really gets me thinking is the code gymnastics...
mobius
"each trigger fire is a performance hit"? Wouldn't you first want to know how many thousands of inserts per second those tables will experience. If it's 20 per DAY, it's a fully mooted argument.
Stephanie Page
A: 

It sounds like you have an is-a type relationship that needs to be modeled. All of the items that can be favourited are a type of "item". It sounds like you are on the right track, but I wouldn't use triggers. What could be the right answer if I have understood correctly, is to pull all the common fields into a single table called items (master is a poor name, master of what?), this should include all the common data that would be needed when you need a users favourite items, I'd expect this to include fields like item_id (primary key), item_type and human_readable_name and maybe some metadata about when the item was created, modified etc. Each of your specific item types would have its own table containing data specific to that item type with an item_id field that has a foreign key relationship to the item table. Then you'd wrap each item type in its own insertion, update and selection SPs (i.e. InsertItemCheese, UpdateItemMonkey, SelectItemCarKeys). The favourites table would then work as you describe, but you only need to select from the item table. If your app needs the specific data for each item type, it would have to be queried for each item (caching is your friend here).

If MySQL supports SPs with multiple result sets you could write one that outputs all the items as a result set, then a result set for each item type if you need all the specific item data in one go. For most cases I would not expect you to need all the data all the time.

Steve
It has crossed my mind but in the end I think that if I follow your approach I am risking destroying the integrity of my database and making it unnormalized. And to avoid that I should have transaction on all my update/delete statements...
mobius
How does it violate the integrity of your db? You wrap everything in an SP so that you can stick everything in a BEGIN TRANSACTION/END TRANSACTION. Also, you can enforce the 1-1 mapping with constraints, and (if MySQL supports it) use cascading deletes on the Foreign Key constraints.
Steve
+1  A: 

One way wold be to sub-type all possible tables to a generic super-type (Entity) and than link user preferences to that super-type. For example:

alt text

Damir Sudarevic
A: 

I think you're on the right track, but a table-based inheritance approach would be great here:

Create a table master_ids, with just one column: an int-identity primary key field called master_id.

On your other tables, (users as an example), change the user_id column from being an int-identity primary key to being just an int primary key. Next, make user_id a foreign key to master_ids.master_id.

This largely preserves data integrity. The only place you can trip up is if you have a master_id = 1, and with a user_id = 1 and a post_id = 1. For a given master_id, you should have only one entry across all tables. In this scenario you have no way of knowing whether master_id 1 refers to the user or to the post. A way to make sure this doesn't happen is to add a second column to the master_ids table, a type_id column. Type_id 1 can refer to users, type_id 2 can refer to posts, etc.. Then you are pretty much good.

Code "gymnastics" may be a bit necessary for inserts. If you're using a good ORM, it shouldn't be a problem. If not, stored procs for inserts are the way to go. But you're having your cake and eating it too.

Shlomo
A: 

Keep in mind that not EVERY use of a PK column needs a constraint. For example a logging table. Even though a logging table has a copy of the PK column from the table being logged, you can't build a constraint.

What would be the worst possible case. You insert a record for Oprah's TV show into the favorites table and then next year you delete the Oprah Show from the list of TV shows but don't delete that ID from the Favorites table? Will that break anything? Probably not. When you join favorites to TV shows that record will fall out of the result set.

There are a couple of ways to share values for PK's. Oracle has the advantage of sequences. If you don't have those you can add a "Step" to your Autonumber fields. There's always a risk though.

Say you think you'll never have more than 10 tables of "things which could be favored" Then start your PK's at 0 for the first table increment by 10, 1 for the second table increment by 10, 2 for the third... and so on. That will guarantee that all the values will be unique across those 10 tables. The risk is that a future requirement will add table 11. You can always 'pad' your guestimate

Stephanie Page
Well you are pretty much right, however imagine that on a large scale you might end up with a huge Favorites table that doesn't join to anything (say you have 20.000 users that have favorite'd the Oprah Show and when you deleted you are left with 20.000 useless rows on your table)
mobius
The idea of having different auto increment steps for the id's, I think can be problematic in a mySQL, especially if it comes to having a master-master/master-slave implementation, where mysql usually performs such steps on auto_increment keys to avoid id collision....
mobius
And you can clean up those useless rows at ANY convenient time. Nothing in my suggestion says that you HAVE to leave them. The point is that the clean up isn't REQUIRED to be part of the business transaction. In a high volume system that's important.
Stephanie Page
I'm not clear on the second comment. You're saying that in Master-Slave implementations the slave can generate new rows? Or that the INCREMENT BY function is co-opted by the system and therefore off-limits for development. Also, are YOU going to use Master/Master or Master/Slave or is that argument a theoretical one?
Stephanie Page