I've created a nested commenting system like so:
id
user_id
comment
comment_date
parent_comment_id
And I am trying to get a thread of comments by there parent ids. My query is as such:
with recursive cte (id, user_id, comment, comment_date, parent_comment_id) as (
select id,
user_id,
comment,
comment_id,
parent_comment_id
from comments
where parent_comment_id = 'MES-738fc5be20b24b57978b3e873237ef12'
union all
select c.id,
c.user_id,
c.comment,
c.comment_date,
c.comment_parent_id
from comments c
inner join cte
on c.parent_comment_id = cte.id
)
select * from cte
The works except it does not return the first parent comment, which has a parent_comment_id of null. What could I be doing wrong?
I am guessing that you simply want a different anchor:
select id, user_id, comment, comment_id, parent_comment_id
from comments
where id = 'MES-738fc5be20b24b57978b3e873237ef12'
Related
I often have a situation with two tables in MySQL where I need one record for each foreign key. For example:
table post {id, ...}
table comment {id, post_id, ...}
SELECT * FROM comment GROUP BY post_id ORDER BY id ASC
-- Oldest comment for each post
or
table client {id, ...}
table payment {id, client_id, ...}
SELECT * FROM payment GROUP BY client_id ORDER BY id DESC
-- Most recent payment from each client
These queries often fail because the "SELECT list is not in GROUP BY clause" and contains nonaggregated columns.
Failed Solutions
I can usually work around this with a min()/max() but that creates a very slow query with mis-matched results (row with min(id) isn't equal to row with min(textfield))
SELECT min(id), min(textfield), ... FROM table GROUP BY fk_id
Adding all the columns to GROUP BY results in duplicate records (from the fk_id) which defeats the purpose of GROUP BY.
SELECT id, textfield, ... FROM table GROUP BY fk_id, id, textfield
Same idea as #GurV but using a join instead of a correlated subquery. The basic idea here is that the subquery finds, for each post which has comments, the oldest post and its corresponding id in the comments table. We then join back to comments again to restrict to the records we want.
SELECT t1.*
FROM comments t1
INNER JOIN
(
SELECT post_id, MIN(id) AS min_id
FROM comments
GROUP BY post_id
) t2
ON t1.post_id = t2.post_id AND
t1.id = t2.min_id
You can use a correlated query with aggregation to find out the earliest comment for each post:
select *
from comments c1
where id = (
select min(id)
from comments c2
where c1.post_id = c2.post_id
)
Compound index - comments(id, post_id) should be helpful.
If you are querying the whole table with many rows, then it will. This query is more useful and performant if you are querying for a small subset of posts. If you are querying the whole table, then #Tim's answer is better suited I think.
I'm creating a very basic post and reply system to get a better understanding of MySQL and PHP. I have two tables: posts and comments.
posts(post_id, post_user, timestamp, post_text)
comments(comment_id, post_id, timestamp, comment_text)
What I want to do is order the posts by the ones that have the most recent reply. So I would need to SELECT * from posts ordered by comments.timestamp desc since I want to order by most recent comments and not by the original post's timestamp. I can't figure out a proper query that works.
You may looking for this
SELECT p.*
FROM posts p
INNER JOIN comments c ON c.post_id= p.post_id
ORDER BY c.timestamp desc
SELECT post_id, post_user, timestamp, post_text,
most_recent_comment
FROM posts NATURAL JOIN
( SELECT post_id,
MAX( timestamp ) AS most_recent_comment
FROM comments
GROUP
BY post_id ) AS t
UNION
SELECT post_id, post_user, timestamp, post_text,
NULL AS most_recent_comment
FROM posts
WHERE post_id NOT IN ( SELECT post_id FROM comments );
SELECT A.Post_Id FROM
(SELECT P.Post_Id,C.TimeStamp,ROW_NUMBER() OVER( PARTITION BY S.Post_Id ORDER BY C.TimeStamp DESC) Rnk FROM POSTS p INNER JOIN COMMENTS C
ON P.Post_Id=C.Post_Id) A
WHERE A.Rnk=1
ORDER BY A.TimeStamp DESC
This is SQL SERVER version.
So hope you can find Mysql version for it
Let's assume i have 4 tables:
'users' (id, username),
'photos' (id, user_id, name),
'photos_comments' (id, photo_id, user_id, text),
'photos_likes' (id, photo_id, user_id, test).
I want to calculate sum of all comments and likes for every user in all of his uploaded photos. For that i'm trying to build a query:
SELECT users.*,
(SELECT SUM(count) as rating FROM(
SELECT COUNT(*) as count FROM photos_likes
WHERE photos_likes.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)
UNION
SELECT COUNT(*) as count FROM photos_comments
WHERE photos_comments.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)
) as total_rating) as rating FROM users
It returns 'Unknown users.id column in WHERE clause' error. So it looks like it can't see users table in most inner query.I can't understand why it happens,because another similar query works ok:
SELECT users.*,
(SELECT COUNT(*) as count FROM photos_likes
WHERE photos_likes.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)) as likes_count,
(SELECT COUNT(*) as count FROM photos_comments
WHERE photos_comments.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)) as comments_count FROM users
In this query it can grab id from users table row in most inner query. Why is it working like that? Thanks for help.
Look into Subqueries in the FROM Clause:
Subqueries in the FROM clause cannot be correlated subqueries, unless used within the ON clause of a JOIN operation.
In your second example, you use the subquery in a where clause. That's the difference.
See also Correlated Subqueries.
select
photos.userid,
photos.photoid,
count(distinct commentid),
count(distinct likeid),
count(distinct commentid) + count(distinct likeid) as total
from
photos
left join photos_comments on photos.photoid=photos_comments.photoid
left join photos_likes on photos.photoid=photos_likes.photoid
group by photos.userid, photos.photoid
Here is what I am looking to do but I have my doubts if its possible or not!
I have 2 tables, one is called users and the other is answers. I need to combine the results into a single statement, and order the table by time. the tricky part is that I have time on both tables and I need to take both time fields into account for ordering the results.
This is my unsuccessful attempt to come up with the query:
SELECT * FROM ( SELECT id, username, first_name, last_name, time FROM users
UNION ALL
SELECT id, answer, pid, uid, time FROM answers ) as AB
ORDER BY `AB.time`
UPDATE
I have come up with the following SQL Query:
SELECT username AS user, '' as page , '' as content , time FROM users
UNION ALL
SELECT (SELECT username FROM users WHERE `id`=`uid`) AS user, pid AS page, answer AS content, time FROM answers
ORDER BY `time` DESC
its what I am looking for but for some reason the user field in the second SELECT query is showing as null! any idea why?
SELECT id, username, first_name, last_name, time FROM users
UNION ALL
SELECT id, answer, pid, uid, time FROM answers
ORDER BY 'time`
solution to have different column name for each table
SELECT id AS t1_id,
username AS t1_username,
first_name AS t1_first_name,
last_name AS t1_last_name,
NULL ast2_id,
NULL AS t2_answer,
NULL AS t2_pid,
NULL AS t2_uid,
time
FROM users
UNION ALL
SELECT NULL AS t1_id,
NULL AS t1_username,
NULL AS t1_first_name,
NULL AS t1_last_name,
id ast2_id,
answer AS t2_answer,
pid AS t2_pid,
uid AS t2_uid,
time
FROM answers
ORDER BY time
If users.id and answers.uid are supposed to be the same then this should work:
SELECT u.*, a.* FROM users AS u
JOIN answers AS a ON a.uid = u.id
ORDER BY a.time
I have the following MySql table (user_actions) with the following columns:
id, user_id, created_at, action_type
I want a sql query that will get the latest actions that the user performed with no duplication of the actions.
for example:
user_id 1 has 3 records that has the action_type "follow"
and 2 records that has the action_type "unfollow"
in this case i want the query to return two records, one with action_type "follow" and one with "unfollow"
any thoughts?
You can use SQL's group by clause for this:
select user_id, action_type, max(created_at)
from user_actions
group by user_id, action_type
try this:
select ua.*
from user_actions ua
join (select max(id) as max_id,user_id,action_type from user_action group by user_id,user_action) ua_max
on ua.id=ua_max.max_id and ua.user_id=ua_max.user_id and ua.action_type=ua_max.action_type
Try this query:
select * from user_actions where action_type, created_at in
(select action_type, max(created_at) from user_actions group by action_type);