COUNT, GROUP BY, INNER JOIN, php, mysql - php

I am currently (trying to) develop a forum. Now I've run into a small obstacle. I've four tables in my database; "categories", "forums", "topics" and "replies". They all have an id, a parent id and a name.
My problem is now how I should store and get the amount of topics and replies for each category and forum. At first I thought of saving all the topics and replies in every category and forum, as well as save all the replies in every topic.
But that is something I'd rather try to avoid seeing as I have to do 1x insert and 3x update for every reply made; 1 to category, 1 to forum, 1 to topic and finally the reply itself, an insert.
So instead I thought if it somehow was possible (and if so how) to make a query with COUNT, GROUP BY and INNER JOIN
Something along the line
SELECT *
FROM `categories`
JOIN (COUNT ( * )
FROM `topics`
GROUP BY `parent_id`
WHERE `parent_id` = `categories`.`id`) as `amount_of_topics`
JOIN (COUNT ( * )
FROM `topics`
GROUP BY `parent_id`
WHERE `parent_id` = `topics`.`id`) as `amount_of_replies`
Just that I don't get it work the way I want to at.
So to fetch all replies and count them for a specific forum, the connection replies and forum got is that parent_id of the reply belongs to a topic whos parent_id belongs to a forum.

I'm not sure if I understand your question correctly but if you just want the count of topics and replies for a specific forum (provided the categories belong to a forum and not the other way around) you would go with a query along this line:
SELECT forums.ID as forum_id
, forums.name as forum_name
, Count(topics.ID) AS count_of_topics
, Count(replies.ID) AS count_of replies
FROM forums
LEFT JOIN categories ON forums.ID = categories.parent_id
LEFT JOIN topics ON categories.ID = topics.parent_id
LEFT JOIN replies ON topics.ID = replies.parent_id
GROUP BY forums.ID, forums.name, categories.ID, categories.name;
If you are looking for the count of topics & replies by categories, use:
SELECT categories.ID as category_id
, categories.name as category_name
, Count(topics.ID) AS count_of_topics
, Count(replies.ID) AS count_of replies
FROM categories
LEFT JOIN topics ON categories.ID = topics.parent_id
LEFT JOIN replies ON topics.ID = replies.parent_id
GROUP BY forums.ID, forums.name, categories.ID, categories.name;
Hope that helps but if that's not what you are trying to achieve, please add precisions to your question.

I think this is the query you want:
SELECT categories.*, `amount_of_topics`, `amount_of_replies`
FROM `categories` JOIN
(select `parent_id`, COUNT ( * ) as `amount_of_topics`
FROM `topics`
GROUP BY `parent_id`
) tp
on tp.`parent_id` = `categories`.`id`) JOIN
(select `topics`.`id`) , COUNT ( * ) as `amount_of_replies`
FROM `topics`
GROUP BY `topics`.`id`
) ti
on tp.`parent_id` = ti.`id`
The idea is to create separate subqueries for each of the topic groups you want, and then to join them together.

Related

PHP/MySQL: A single SQL query to fetch forum name, sum total of topics, posts and the LAST POST (post id, topic id and username)

Ok, so this is not a new problem to me but since sophisticated SQL queries is not among my best talents I would typically break it apart. Still, I'm pretty sure a single query is possible and this time around I feel like doing it right but I simply can't put my finger on it. The following is as far as I could go:
SELECT forums.forum, forums.description, topics.forum_id,
(SELECT COUNT(id) FROM topics WHERE forum_id = forums.id) AS num_topics,
(SELECT COUNT(id) FROM posts WHERE topic_id = topics.id) AS num_posts,
MAX(posts.id) AS last_post
FROM forums
LEFT JOIN topics ON topics.forum_id = forums.id
LEFT JOIN posts ON posts.topic_id = topics.id
LEFT JOIN users ON users.id = posts.author_id
So I select forum id, forum name, short description and forum id aqcuired from topics table because the column name is unique by default. Then I use subqueries to count the sum total of topics and posts per forum (is there any other way to do it?). And then comes the most tricky part. I can get the last post per forum (MAX(post.id)) but try as I might I can't get the topic id and name for it, as well as the post author, which I never even got close to. Doing something like (SELECT topic_id FROM posts WHERE id = last_post) is not working, of course, and I don't understand why.
This will not be quick
SELECT forums.
forum, forums.description, topics.forum_id,
(SELECT COUNT(id) FROM topics WHERE forum_id = forums.id) AS num_topics,
(SELECT COUNT(id) FROM posts WHERE topic_id = topics.id) AS num_posts,
MAX(posts.id) AS last_post,
(SELECT topic_id FROM posts ORDER BY id DESC LIMIT 1) LAst_topic_id,
(SELECT topic_name FROM topics WHERE id = (SELECT topic_id FROM posts ORDER BY id DESC LIMIT 1)) LAst_topic_name
FROM forums
LEFT JOIN topics ON topics.forum_id = forums.id
LEFT JOIN posts ON posts.topic_id = topics.id
LEFT JOIN users ON users.id = posts.author_id
Or if the last id is not really the last id from the post tables
SELECT
forum,description,forum_id,num_topics,num_posts,last_post,
(SELECT topic_id FROM posts WHERE id = last_post) LAst_topic_id,
(SELECT topic_name FROM topics WHERE id = (SELECT topic_id FROM posts WHERE id = last_post)) LAst_topic_name
FROM
(SELECT
forums.forum, forums.description, topics.forum_id,
(SELECT COUNT(id) FROM topics WHERE forum_id = forums.id) AS num_topics,
(SELECT COUNT(id) FROM posts WHERE topic_id = topics.id) AS num_posts,
MAX(posts.id) AS last_post
FROM forums
LEFT JOIN topics ON topics.forum_id = forums.id
LEFT JOIN posts ON posts.topic_id = topics.id
LEFT JOIN users ON users.id = posts.author_id) t1;

How can I order topics by post.date_written?

I have 2 tables(I'm making kind of forum) I would like to sort topics order by last post that has been written(order by row 'posts.date_written'). I was trying to do it by myself but I couldn't get it working.
Topic table:
ID|id_subforum|owner_id|owner|title|description
Posts table:
ID|id_subforum|id_topic|by|description|date_written
Can somebody show me how should this SQL look like?
SELECT t.*
FROM topics AS t
LEFT JOIN (
SELECT id_topic, id_subforum, MAX(date_written) AS last_date
FROM posts
GROUP BY id_topic, id_subforum
) AS p
ON t.id = p.id_topic AND t.id_subforum = p.id_subforum
WHERE t.id_subforum = '$forumId'
ORDER BY last_date DESC

List comments by number of likes

I'm trying to get PHP to list out comments by descending number of likes they receive.
Currently, the comments' content and the number of likes they receive are in 2 separate tables: "comments" and "likes".
PHP code:
To get comments from "comments" table:
$this->db->order_by ('comment_id', 'asc');
$data['comment'] = $this->db->select()->get('comment');
To get likes from "likes" table:
$data['like'] = $this->db->get('like');
To show the number of likes for each comment:
$query_like=$this->db->query("select ip from like where comment_id='$comment_id'");
$count_like=$query_like->num_rows();
I'm wondering if it's possible to order the comments by the number of likes they receive without changing the tables' structure. Any advice hugely appreciated.
If I understand the data structure correctly, you just need a join and an aggregation:
select c.*, count(*) as numlikes
from comments c join
upvote l
on c.comment_id = l.comment_id
group by c.comment_id
order by count(*) desc;
EDIT:
To get comments with zero upvotes, do the left outer join in the other direction:
SELECT c.*, count(u.comment_id) as num_upvotes
FROM comment c left join
upvote u
on c.comment_id = u.comment_id
WHERE c.comment_id = '$interview_id'
GROUP BY c.comment_id
ORDER BY num_upvotes DESC;

PHP & Mysql - Left Outer Join between two tables

I have two tables called 'events' and 'topics' each table can have many comments.
What I need to do is list all the events and topics with the amount of comments for each row. I've managed to return all the topics, which works great but I don't know how I can add the events table to the MySql. The comments and events table fields are listed below. Can anyone help me with this query?
Events:
ID
Event_Name
Comments:
post_id <-- the releated id for either the events or topics table
table <-- The table that the row belongs to so either topics or events
SELECT
t.id, t.title, c.created_at,
IF(ISNULL(c.allComments), 0, c.allComments) AS totalComments
FROM topics AS t
LEFT OUTER JOIN (
SELECT created_at, post_id, COUNT(*) AS allComments
FROM comments
GROUP BY post_id
) AS c ON c.post_id = t.id
ORDER BY tc.created_at DESC, c.allComments DESC
Sounds like events and topics should be the same table.
Still, I think we can do this with a UNION. Events and Topics have the same columns i hope? (Or at least the same important ones?)
(SELECT c.table as event_or_topic, e.*, count(C.table), MAX(C.created_at) as latest_c
FROM events E LEFT JOIN comments C on (C.post_id = E.id)
WHERE C.table = 'Events'
GROUP BY C.post_id)
UNION
(SELECT c.table as event_or_topic, t.id*, count(C.table), MAX(C.created_at) as latest_c
FROM topics T LEFT JOIN comments C on (C.post_id = E.id)
WHERE C.table = 'Topics'
GROUP BY C.post_id)
ORDER BY latest_c
Notice that the ORDER BY applies to the whole UNION, not the individual SELECTs.
The use of LEFT JOIN should allow those rows without Comments to still show. I think the problem is that we have parts of our select dependent on comments (ie - C.table, ordering on last comment, etc). The count should be fine - will just be zero if there are no comments.
You might need to change the SELECT part slightly. I'd like to display C.table so you know whether a row is a topic or event, but im afraid it might screw up the count. Do you need anything from comments besides the count? You use some columns other than post_id and table in your query that you neglected to explain in your question.
You still have columns I don't know what they are, like Comment's zoneTable
Try this:
SELECT
t.id, t.title, c.created_at, COUNT(c.allComments) AS totalComments
FROM topics AS t LEFT JOIN comments c ON t.id=c.post_id
GROUP BY t.id ORDER BY tc.created_at DESC, c.allComments DESC
If I understand your question you have 3 tables:
-Events
-Topics
-Comments
If that is true something like this should extract all the data:
SELECT *
FROM events,topics
LEFT JOIN comments ON post_ID = ID
ORDER BY date DESC
Hope i'm along the right lines!
W.
I've got it working. If anyone knows of a better and an efficient way of doing this, then please let me know:
(SELECT t.id, t.title, tc.dateCreated AS commentDate,
IF(ISNULL(tc.allComments), 0, tc.allComments) AS totalComments,
t.LastActive as dateChanged
FROM Events AS t
LEFT OUTER JOIN (
SELECT MAX(created_at) AS dateCreated, post_id,
COUNT(*) AS allComments
FROM comments
GROUP BY post_id
) AS tc ON tc.post_id = t.id)
UNION
(SELECT t.id, t.title, tc.dateCreated AS commentDate,
IF(ISNULL(tc.allComments), 0, tc.allComments) AS totalComments,
t.LastActive as dateChanged
FROM topics AS t
LEFT OUTER JOIN (
SELECT MAX(created_at) AS dateCreated, post_id,
COUNT(*) AS allComments
FROM comments
GROUP BY post_id
) AS tc ON tc.post_id = t.id)
ORDER BY commentDate DESC, dateChanged DESC, totalComments DESC

SQL- how to extract forum topics that are not repeats?

On the home page of my website I want to display the latest posts to the forum however I don't want to show the same topic twice. How can I modify the code below to do this?
http://punbb.informer.com/wiki/punbb13/integration#recent_10_posts
Basically show the latest posts, but only once for each forum topic/thread.
Add a condition to keep only records where the post is the last post in the topic:
WHERE p.id = (
SELECT pp.id
FROM posts AS pp
WHERE pp.topic_id = t.id ORDER BY pp.posted DESC LIMIT 1
)
If you want only one value per topic, you could group by topic, and from each topic select the most recent post. Then, you could choose the top 10 topics.
I'll write it in SQL, and you can translate that to PHP:
SELECT p.id, p.message, o.subject
FROM
((SELECT t.id
FROM posts AS p LEFT JOIN topics AS t ON p.topic_id = t.id
GROUP BY t.id
HAVING p.posted = MAX(p.posted) ) ids LEFT JOIN topics AS t ON ids.id = t.id) o
LEFT JOIN posts AS p ON o.id = posts.topic_id
ORDER BY p.posted DESC
LIMIT '0,10'
change this line
'SELECT' => 'p.id, p.message, t.subject',
to
'SELECT DISTINCT' => 'p.id, p.message, t.subject',

Categories