views:

160

answers:

4

Basically I want to create a comment system where comments may have parents that are also comments BUT I would also like them to potentially have parents that may be something else, such as users or products (ie, I want to be able to comment on products, users, other comments, or practically any resource)

How should I do that?

Current tables:

tags, products, users, comments

edit - this would be for a somewhat high traffic site, so I can't have it doing all kinds of craziness :-)

A: 

maybe

CREATE TABLE comment (
id INT PK,
parent_comment INT NULL FK,
content TEXT,
table_source VARCHAR(30), -- SYSNAME,
row_source INT,
)

In table_source you would save the table source (product, user, etc), and in row_source, the id of the row the comment is pointing.

Jhonny D. Cano -Leftware-
I prefer this approach, and we use a similar structure for creating audit records for our large SaaS application.I would normalize the data further by replacing table_source with a FK to a lookup table of tablenames, or even the object_id from sys.tables
Jeff Fritz
You still cannot bind a comment to a particular record in any table, as you'll be storing the primary keys from multiple tables here. You also can't deal with compound primary keys.
Adam Robinson
The approach Adam points is equally valuable, this is just a different solution, Maybe Jack doesn't wan't to bother with a lot of foreign key relationship tables, and maybe he wants a dynamic design
Jhonny D. Cano -Leftware-
Jeff, I do like the idea of a FK to a lookup of tables because I have no idea whether the number of tables will stay fixed (in fact, it's entirely likely that tables may be added programmatically.)
this isn't a good solution for retrieving the comments. how would you index this and query on it?
KM
I don't see why this would be any harder to index or query than the other solutions. Can you elaborate on that? This is similar to the approach used in the Polymorphic behavior for CakePHP: http://bakery.cakephp.org/articles/view/polymorphic-behavior
Calvin
the table_source sysname column takes 512 bytes, add 4 more for row_source and that is a big fat index. what if a table has a composite key?
KM
Ah, I didn't realize that sysname was such a large data type. The polymorphic behavior uses a much smaller varchar(30) column to store the classname. Thanks for the explanation.
Calvin
A: 

Your best bet would be isolating the comments from the targets. Something like...

comment:
    comment_id (PK),
    user_id (FK),
    date,
    comment,
    parent_comment_id (FK)

Then tables like...

product_comment:
    product_comment_id (PK),
    product_id (FK),
    comment_id (FK, unique)

Where only the root comments (no parent) would have a row. This would allow you to still maintain a strong foreign-key architecture all around and still only be able to associate a comment to one product.

Adam Robinson
A: 

my try:

CREATE TABLE Comment
(
    CommentID               INT            NOT NULL IDENTITY(1,1) PRIMARY KEY
   ,CommentValue            VARCHAR(5000)  NOT NULL
   ,CommentParentCommentID  INT            NULL     --fk to self
   ,CommentParentTagID      INT            NULL     --fk to Tags
   ,CommentParentProductID  INT            NULL     --fk to Parents
   ,CommentParentUserID     INT            NULL     --fk to Users

)

this will allow for you to find them using an index, without too much waste with storage

KM
You will need to modify the Comment table if you want to attach comments to other resources in the future as Jack has stated (ending up with 10-20 FK columns potentially). But I don't know enough about database design and running high volume sites to say if that is actually a problem or not.
Calvin
in his original question he never mentions 10-20, just the 3
KM
+4  A: 

Do you want to have comments on products, users, reviews, etc? Or find the products, users, reviews, etc, that a comment is referring to?

For the former, I would have tables to associate things with their comments:

create table join_products_comments (
   product_id int (unique, i.e., one thread of comments per product),
   comment_thread_id int
);

create table join_users_comments (
   user_id int (unique, i.e., one thread of comments per user),
   comment_thread_id int
);

Where a comment_thread is just a reference to a thread that every comment references:

create table comment_threads (
    thread_id int (PK),
    thread_name nvarchar2(256),
    created datetime
);

create table comments (
    comment_id int (PK),
    comment_thread_id int (FK),
    parent_comment_id int (FK),
    user_id int (FK), -- person who posted the comment
    comment text,
    created datetime
);

So every commentable entity in the system would have a join table and one comment_thread just waiting for eager users to add comments to. Or you could just link to a root comment instead and do without that indirection.

JeeBee
This seems like a sound approach. I would probably also include the comment_threads table in the answer just for clarity's sake.
Calvin
Yeah, added for clarity.
JeeBee