将一个表连接到许多表

I am building a commenting system where people can comment on uploaded files, messages and to-do items. What is the best way to connect the comment table table to the other various tables?
Possible Solutions
Solution one - use a two field foreign key.

CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
foreign_key INT NOT NULL,
table_name enum('files','messages','to-do'),
user_id INT NOT NULL,
comment TEXT NOT NULL);

Solution two - Each table would have a primary key unique to the database. So I would use php's uniqid($prefix) as the primary keys for each table.

CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
foreign_key char(23) NOT NULL,
table_name enum('files','messages','to-do'),
user_id INT NOT NULL,
comment TEXT NOT NULL);

Solution Three - Have multiple foreign keys in the comment table

CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
files_id INT NOT NULL,
messages_id INT NOT NULL,
to_do_id INT NOT NULL,
user_id INT NOT NULL,
comment TEXT NOT NULL);

What is the best solution? I appreciate your input and please let me know if I can clarify anything

EDIT removed table_name from solution three as it was a copy_paste error As to Joe's Response

Assume: 1) all data is already escaped. Do we really need to see that?
2) $fileId = "146".
3) $userId = "432".
4) $comment = "Stackoverflow is so awesome!"

INSERT

$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
    die('Could not connect: ' . mysql_error());
}
 mysql_select_db('mydb');
 mysql_query("INSERT INTO `comments` (user_id,comment) VALUES($userId,$comment)");
 $commentId = mysql_insert_id();
 mysql_query("INSERT INTO `comments_files_xref` (file_id,comment_id)         VALUES($fileId,$commentId)");

Personally, I would normalize the design a bit more. Perhaps something like:

alt text

I just learn Ruby on Rails in my current company, and solution 1 is preferred because RoR's Active Record can handle it as polymorphic relation.

Back to the topic, that you are using PHP, I prefer either solution 1 or 3. Solution 1 is preferable if there are possibilities that the comment table will be used for other table in the future.

One note, in solution 3, I think the table_name column is not needed. You can determine for which table the comment is by fill either files_id, messages_id, or to_do_id with the id, then set 2 other foreign key with 0.

I would create a join table for each thing that can be commented on, so an files_comments table and a todo_comments table. But solution 1 would be an alternative. I would avoid solutions two or three... could get messy if things change in the future.

Multiple remarks :

  • You shouldn't call your foreign key foreign_key because a foreign key is a constraint, not a field in a way. it references a field in a table to an index on another, call it the same way you called the PK on the table you reference, or something recognizable.
  • Foreign keys constraints only works on innodb, if you use MyISAM forget about them and do a lot of checks with PHP.
  • read http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
  • You should make a sketch of what you want in your DB or use tools like mysql workbench (which is free) that helps seeing the schema better.

As I see your problem and if you want to use constraint here, I'll use solution one or another solution :

1-

CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, // Which is your comment index
idTable INT NOT NULL, // ID of the message
table_name enum('files','messages','to-do'), // which it comes from
user_id INT NOT NULL, // etc...
comment TEXT NOT NULL);

But there are conditions :

  • The PK of files, messages _to-do_ must have the same format (INT)
  • If you want to add a module (to files, messages _to-do_) it'll be difficult

2- Create tables joining comments and other tables :

CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, // Which is your comment index
user_id INT NOT NULL, // etc...
comment TEXT NOT NULL);

CREATE TABLE `comments-files`(
id_comments INT NOT NULL PRIMARY KEY,
id_files INT NOT NULL PRIMARY KEY);

etc. Hope you see the point here. You add constraint thanks to http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html if needed.