Mysql - Ordering Facebook posts, comments, and replies correctly - mysql

Can't seem to find a good answer for this. I currently have two tables, one with Facebook posts, the other with comments. I now need to add replies in addition to this, since FB recently did this.
My current query selects from the posts and joins to the comments. What I'm hoping to do for the replies is to add another entry in the comments but with a parent ID. Whatever query I end up having, I would like the results to look like this:
postID commentID parentID
1
2 1
2 2 1
2 3 1
3 4
So post 1 has no comments, post 2 has one comment with two replies to that comment, and post 3 only has one comment. In my comments table, comment 1-4 are all separate entries in the same table. Is there anyway to do this with one query and not having to have another join to the comments table?
Edit, current query. This query doesn't take care of replies, it's only for posts and one level of comments.
select facebookFeeds.*, facebookComments.userID, facebookComments.name, facebookComments.message as cMessage, facebookComments.createdTime as cCreatedTime from facebookFeeds left join facebookComments on facebookFeeds.id = facebookComments.feedID where facebookComments.accountID= 24 order by createdTime desc

I figured it out, have it working using an if in the order clause. The only disadvantage is that the parent comment ID must be a lower number than its replies. Otherwise, the replies will show before the parent comment. When receiving the data, the replies come after the comment, so it should be fine.
select facebookFeeds.*, facebookComments.id as cID, parentID, facebookComments.userID, facebookComments.name, facebookComments.message as cMessage, facebookComments.createdTime as cCreatedTime from facebookFeeds left join facebookComments on facebookFeeds.id = facebookComments.feedID where facebookComments.accountID = 24 order by facebookFeeds.createdTime desc, if(parentID is null, cID, parentID)

Related

Database schema design for posts, comments and replies

In my previous project I had posts and comments as two tables:
post
id
text
timestamp
userid
comment
id
message
timestamp
userid
postid
Now I've got to design replies to comments. The replies is just one level, so users can only reply to comments, not to replies. The tree structure is only 1 level deep. My first idea was to use the same comment table for both comments and replies. I added a new column though:
comment
id
message
timestamp
userid
postid
parentcommentid
Replies have parentcommentid set to the parent comment they belong. Parent comments don't have it (null)
Retrieving comments for a given post is simple:
but this time I need another query to find out the comment replies. This has to be done for each comment:
This doesn't seem to be a good solution, is there a way to have a single query which returns the complete list of comments/replies in the correct order? (dictated by the timestamp and the nesting)
You may use join and achieve result in single query like I provided below:
SELECT *, cc.message as replied_message
FROM `post`
JOIN comment as c
ON c.postid = post.id
JOIN comment as cc
ON cc.id = c.parentcommentid
ORDER BY c.timestamp DESC, cc.timestamp DESC;
Please note that, it works correctly only if 1 comment have 1 reply only.multiple replies on single comment will not support by this query
I know this reply is years too late, but hopefully it will help others facing this problem now.
I came up with single table and a single query that returns all the results in the correct order that I use on my own site, it's slightly different but the logic could be used to fit this question.
table name : comments
id / varchar(32)
userid / int(10)
comment / text
ordering / int(10)
ordering_secondary / int(10)
source / tinyint(4)
state / tinyint(4)
created / timestamp
edited / timestamp
There is a primary key on (id, ordering, ordering_secondary, source), so no duplicate of these four columns combined will insert.
Inserting a comment you first check "ordering" and increment the new comment by 1.
SELECT ordering FROM comments WHERE id="page id" AND source=0 ORDER BY ordering DESC LIMIT 1
"source" column will be "0" for parent, "1" for a reply for example. So just insert the comment with the ordering value incremented by 1 for each comment on a specific id.
When inserting a reply comment, use the same "ordering" value as the parent but increment the "ordering_secondary" column.
SELECT ordering FROM comments WHERE id="page id" AND source=1 AND ordering="parent comment ordering" ORDER BY ordering DESC LIMIT 1
So the data would look like :
In the table there are two parent comments and two replies to the second parent comment. No replies to first parent comment.
This approach is obviously slightly more overhead on inserts as you have to look up the ordering value of the last comment on an "id" but querying the data is simple.
SELECT * FROM comments WHERE id=? ORDER BY ordering DESC, ordering_secondary ASC LIMIT 30
If you're using a database that supports JSON or object aggregation, you can get a nicer result from the query where each top-level comment is a row (and is not duplicated), and the replies are nested in an array/JSON within each row.
This gives you flexibility with what you do with it and also makes it easier to ensure the ordering and nesting is correct.
An example using Postgres:
SELECT
p.id AS post_id,
c.id AS comment_id,
c.message,
JSON_AGG(
JSON_BUILD_OBJECT('comment', r.comment, 'timestamp', r.timestamp)
ORDER BY r.timestamp
) AS child_comments
FROM
post AS p
INNER JOIN comment AS c
ON c.post_id = p.id
LEFT JOIN comment AS r
ON r.parent_id = c.id
WHERE
post.id = <some id>
AND c.parent_id IS NULL
GROUP BY
post.id,
c.id,
c.message
ORDER BY
c.timestamp DESC
;
Note that, as above, this example will only retrieve the top-level and their first-level replies. It won't get replies to replies. You can use recursive commands or additional subqueries to do that.

How to make a selection of posts from the database with the condition?

How to choose the posts from the database with the condition. Already 2 days of head scratching do not understand how to win the query) I have 3 tables:
I need to choose the posts as shown on the picture:
I get to make a selection of all positions whose pivot based table has a status of checked without conditions. But you must choose the post as shown in the picture, for example a post with id 39 if his column cheked status is checked in all rows. This means that all users have approved the post and it should show. Please tell me how to do such a condition in the query?
SELECT p.*
FROM posts p
WHERE NOT EXISTS( SELECT 'a'
FROM post_user pu
WHERE pu.post_id = p.id
AND pu.checked = 'notChecked'
)

Mysql Left join multiple 1 to many tables

I am trying to get the following data from 3/4 tables in 1 Mysql query, wondering if it is possible ? The tables are
TOPIC
topicid (FK)(PK)
groupid
topic
user
LIKED
likeid
topicid (FK)
user
COMMENT
commentid (PK)
topicid (FK)
comment
user
I write my topics and store in TOPIC Table with unique topicid. I group each topic using groupid.
Other tables may have 0 or more data per topicid.
I am trying to get each topic for a particular group and also get other datas from the concerned Tables. I checked How to left join multiple one to many tables in mysql? and got few idea but that is for the count while I wanted to get details from that table (users who like), and (user and their comment).
I have tried
SELECT t.topicid,
topic,
group_concat(DISTINCT likeid,l.user SEPARATOR '|'),
group_concat(DISTINCT commentid,comment,c.user SEPARATOR '|') AS comments
FROM TOPIC t
LEFT JOIN LIKE l ON l.topicid = t.topicid
LEFT JOIN COMMENT c ON c.topicid = t.topicid
WHERE t.groupid='some_value'
GROUP BY t.topicid
While this works partly e.g. I do get the details but only if there is one topic in a group. If there are 2 or more topics in a group then the concat details are stored in the first record only and the later topics show no likes and comments.
Can someone please help me to correct this or any particular Mysql function I am missing
I am very very sorry for wasting your time, after thorough re-check I found my table data were wrong (checked after making sqlfiddle thanks #Barmar).
I was by mistake inserting wrong data in like and comment table. So Likes and comments for 2nd topic topicid='2' of groupid='1' were inserted by mistake as topicid='1' that is why the details only showed in 1st topic and nothing came out for second topic.
The SQL above is absolutely correct and thankyou for helping me find the fault.
Extremely sorry for posting again.

MySQL Select Newest Approval Date among article date & comment date

My aim is to write the last modified date (lastmod) of my approved articles for my sitemap.xml file. There are 2 possibilities for this data.
If article has no approved comment, then lastmod is the approval
date of the article.
If article has at least 1 approved comment, then lastmod is the
approval date of the last approved comment of related article.
after asking php unsuccessful while loop within prepared statements question and reading answers, I decided
not to use loop within prepared for this case
add col_article_id column to my comments table which shows the
related article's id in article table
and try to solve my case with a smarter mySQL query
I have 2 tables:
articles
comments (comments.col_article_id links the comment to the related
article)
After I tried query below,
select tb_articles.col_approvaldate, tb_comments.col_approvaldate
from tb_articles, tb_comments
where tb_articles.col_status ='approved' AND tb_comments.col_status ='approved' AND tb_articles.col_id=tb_comments.col_article_id
my problems are:
1 - I need someway as if it was allowed in mysql syntax that select max( tb_articles.col_approvaldate, tb_comments.col_approvaldate)
2 - If I have n approved articles without any approved comment and m approved articles with approved comment(s), then I should have n+m result rows with 1 column at each row. But currently, I have m rows with 2 columns at each row.
So I'm aware that I'm terribly on wrong way.
I also searched "mysql newest row per group" keywords. But this the point I could arrived after all.
this is my 1st join experience. can you please correct me? best regards
Try this:
select
tb_articles.id,
ifnull(
max( tb_comments.col_approvaldate),
tb_articles.col_approvaldate
) as last_approved
from tb_articles
left join tb_comments
on tb_articles.col_id=tb_comments.col_article_id
and tb_comments.col_status ='approved'
where tb_articles.col_status ='approved'
group by 1;
Do I understand correctly that tb_comments record for an article is created by default? This should not be the case - if there are no comments, there shouldn't be a record there. If there is, what is the default date you are putting in? NULL? Also tb_comments.col_status seems redundant to me - if you have tb_comments.col_approvaldate then it is approved on that date and you don't really need status at all.
This query is probably what would work for you (commented AND shouldn't change things if I understand your table structure properly):
SELECT
a.col_id AS 'article_id',
IFNULL(MAX(c.col_approvaldate), a.col_approvaldate)
FROM
tb_articles a
LEFT JOIN tb_comments c ON a.col_id = c.col_article_id #AND c.col_status ='approved'
WHERE
a.col_status ='approved'
GROUP BY a.col_id

Retrieve first few comments on a post from MySQL table

You know how the Facebook home feed lists all the recent posts? It shows the user that posted, their actual post and then the first few comments attached to that post. That's what I'm trying to achieve, but I'm having a hard time building a single query that can gather all that data.
I have 3 tables: Uses, Posts and Comments. Each has a unique ID, but they reference each other's IDs. i.e, the Comments table has columns for the user_id of the user who posted and the post_id of the post it is attached to.
At the minute I'm querying SQL to gather all the posts. I join my Users and Comments tables to learn the Username of the poster and a total of how many comments the post has, like so:
$query = "
SELECT `posts`.`id`,`posts`.`message`,`posts`.`link`,
`posts`.`posted`,`posts`.`category`,`posts`.`user_id`,
`users`.`username`,
count(`comments`.`id`)
FROM `posts`
INNER JOIN `users`
ON `posts`.`user_id`=`users`.`id`
JOIN `comments`
ON `comments`.`post_id`=`posts`.`id`
WHERE `posts`.`group_id` = '$id'
AND `posts`.`category`='$filter'
GROUP BY `posts`.`id`
ORDER BY `posts`.`posted`
DESC
";
But instead of finding how many comments a post has, I would instead like to read the first few posts. Can anyone think of a way to achieve this with just the one query?
You can use the LIMIT clause to the the "first" posts. By making the "first posts" a subquery and then joining in the comments you can get everything in a single query. The comments should be left-joined in case of posts with no comments.
Notes:
This query is untested, but it should be close.
This will get all comments for the first few posts, so you'll need to limit the display of "max 3 comments per post" using the front-end display code.
It may be possible to limit comments to 3 per post within this query using variables, but that's not something I know how to do.
Here's the query:
SELECT
FirstPosts.id,
FirstPosts.message,
FirstPosts.link,
FirstPosts.posted,
FirstPosts.posts,
FirstPosts.user_id,
FirstPosts.username,
comments.<< your comment column >>
FROM (
SELECT `posts`.`id`,`posts`.`message`,`posts`.`link`,
`posts`.`posted`,`posts`.`category`,`posts`.`user_id`,
`users`.`username`
FROM `posts`
INNER JOIN `users`
ON `posts`.`user_id`=`users`.`id`
WHERE `posts`.`group_id` = '$id'
AND `posts`.`category`='$filter'
ORDER BY `posts`.`posted` DESC
LIMIT 20) FirstPosts
LEFT JOIN comments ON FirstPosts.id = comments.post_id
ORDER BY FirstPosts.Posted, comments.<< column you use to determing comment order >>
If you determine comment order by a date or sequence, you'll have to ORDER BY FirstPosts.Posted, comments.whatever DESC.
Hope this helps!