tags:

views:

210

answers:

4
+1  Q: 

MySQL Simple Forum

So I'm trying to build a simple forum. It'll be a list of topics in descending order by the date of either the topic (if no replies) or latest reply. Here's the DB structure:

forum_topic

id, name, email, body, date

forum_reply

id, email, body, date, topic_id

The forum itself will consist of an HTML table with the following headers:

Topic, Last Modified, # Replies

What would the query or queries look like to produce such a structure? I was thinking it would involve a cross join, but not sure... Thanks in advance.

+3  A: 

Somewhat like this:

select * from forum_topic
inner join forum_reply on forum_topic.id=topc_id

However, don't use select *

That's bad practice :)

And I don't like the way you avoid normalization! Meaning I would rather have:

Users

  • UserID
  • Name
  • Email

Threads

  • ThreadID
  • Subject
  • Answered
  • AskedByUserID
  • Date

Replies

  • ReplyID
  • ThreadID
  • UserID
  • Answer
  • Date

Then selecting a Thread like this:

select ThreadID, Subject, Answered, AksedByUserID, Date from Threads

And selecting all replies like this

select Answer, Date, Name, Email from Threads
inner join Replies on Threads,ThreaID=Replies.ThreadID
inner join Users on AskedByUserID=UserID 
where Threads.ThreadID=xxx

Now this was just written from the top of my head, but you might need to add some group by as well.

Filip Ekberg
Why did i get downvoted?
Filip Ekberg
There seems to be some harsh downvoting going on indeed. I would guess in your case it's because your query doesn't actually return the data required
Paul Dixon
No idea why you got downvoted. FYI, the columns were named as such for simplicity in explanation :-)
Matt
What i provide is just an example of how a "better" forum would be presented and an example of how to use the joins, downvoting is just evul when trying to provide some solid nice examples.
Filip Ekberg
I figured you wanted to select the forum thread and not a list of all threads, im sorry for the missunderstanding. For that you would use COUNT and have to use a GROUP BY clause.
Filip Ekberg
Also my respons is more of a generic one to show a simplified structure which follow normalization rules.
Filip Ekberg
+1  A: 

Yes, you should be able to get it with a query like this:

SELECT 
  forum_topic.id, 
  forum_topic.name AS Topic,  
  MAX(forum_reply.date) AS Last_Modified, 
  count(*) AS  Replies
FROM forum_topic 
INNER JOIN forum_reply ON (forum_topic.id=forum_reply.topic_id)
GROUP BY forum_topic.id

The "group by" is the magic that gives us one row per topic, with the MAX() and COUNT() functions giving us the aggregated data you need.

(EDIT: I missed that the body of the first post was in the topic table, so posts with no replies would get missed by the above query. Filip has the right idea suggesting you normalize your data. Once normalized, a query similar the above would get you the data you need).

Paul Dixon
You mean MAX(r.date) and not p.date, right?
kigurai
Fixed, downvote a little harsh though!
Paul Dixon
That's close, but with that query, the topic won't appear if it has no replies.
Matt
Damn, yes missed that. Assumed first post was a reply, but as there's a body in the topic....
Paul Dixon
Filip has the right idea with normalizing the data
Paul Dixon
A: 

By "normalized", you mean that the body column of "forum_topic" should be removed, and the actual topic body should be the first reply?

Matt
You can read more about that here: http://www.devshed.com/c/a/MySQL/An-Introduction-to-Database-Normalization/
Filip Ekberg
Normalization bascily means to not repeate yourself when you dont need to. You have for instance "Email" in more than one place, i would rather use a User-table to store that.
Filip Ekberg
Right, it's really a trade-off. Disk space vs. processor time. Joins can be expensive -- normalization isn't always the be-all end-all. In this case, I need for the topic itself to contain a description field. What would it take to modify your (or Paul's) query to show topics, even w/out replies?
Matt
Not very much. You simply do:; select * from Threadsinner join Users on Threads. AskedByUserID=UserID;which will join the Usertable, giving you information about the users and the topic. However with replies you need to join that one too. Twice i'd say if you want the asking user info aswell.
Filip Ekberg
+1  A: 

First off, it seems to me noboody is actually answering your question, which was:

What would the query or queries look like to produce such a structure?

with a requested structure of

Topic, LastModified, # Replies.

The SQL to produce a result table with that structure, given the table structures you provided, would be:

SELECT t.Id, t.Name AS Topic, 
       MAX(r.Date) AS LastModified, 
       COUNT(*) AS NumReplies
FROM Forum_Topic t
LEFT OUTER JOIN Forum_Reply r ON t.id = r.topic_id
GROUP BY t.Id, t.Name

(sorry, this is tested only on SQL Server, as I don't have access to MySql at the moment)

Also, your structure IS already normalized. Suggestions to the contrary are making assumptions about what you want to do, e.g., assuming that you are interested in tracking user names in addition to email addresses. This is quite reasonable, but is nevertheless an assumption. There is nothing wrong, from a normalization perspective, with using email address as a unique user identifier.

Now, if you are looking for general suggestions on how to set up a database, we can give you LOTS of those. Before normalization, I would start with not using potential keywords as object names (e.g., don't give columns names like 'Name' and 'Date').

Regarding the comment from Matt about the value being NULL when there are no replies: using the COALESCE() function will fix that. COALESCE() returns the first non-NULL argument (or NULL if all arguments are NULL). So replace the MAX(r.Date) with MAX(COALESCE(r.Date, t.Date)).

Rob3C
That's 99% there. All topics now appear. The only problem now is that LastModified = NULL when the topic has no replies. Any ideas?
Matt
See the edit to my previous answer.
Rob3C
Filip Ekberg
Rob, you're the man. Works perfectly. Thanks everyone for the help!
Matt