mySQL - Select 2 latest comments for each posts - mysql

I trying to limit for only 2 comments for each posts,i select in the post table and i want to get 2 comments for each
Schema:
Posts table
------------------------------------
id | content | date |
25 | hello | 20/10/2013 |
Comments TABLE
------------------------------------------------
id | content | post | date |
1 | hello | 25 | 20/10/2013 |
Could you help me friends, i'm so confused !
Thanks before, Any help will be appreciate.

This query returns last 2 comments for each post:
SQLFiddle demo
select p.content post_content,
c.content comment_content
from posts p
left join comments c on
(p.id=c.post)
and
c.id>
(select id from comments
where post=p.id
order by id DESC LIMIT 2,1)

Syntax may not be perfect, did not have time to create fiddle. But this has subquery which should get the latest 2 comments related to the post and join that to the post itself. Have to consider that fact that there may be no comments at all hence the test for Is Null from the Left Join.
Select *
From Posts p
Left Outer Join Comments c
On c.post = p.id
Where
( c.id Is Null
Or c.id In
(
Select c2.id
From Comments c2
Where c2.post = p.id
Order by c2.id Desc
Limit 2
)
)

MySQL supports the LIMIT keyword, which allows you to control how many rows are returned; ideal when displaying data over many pages. You can use LIMIT in your sql query like this
In your case
select * from posts p join comments c on p.id=c.post and
c.id> (select id from comments where post=p.id order by id DESC LIMIT 2,1)

Related

Selecting a count of rows having a max value

Working example: http://sqlfiddle.com/#!9/80995/20
I have three tables, a user table, a user_group table, and a link table.
The link table contains the dates that users were added to user groups. I need a query that returns the count of users currently in each group. The most recent date determines the group that the user is currently in.
SELECT
user_groups.name,
COUNT(l.name) AS ct,
GROUP_CONCAT(l.`name` separator ", ") AS members
FROM user_groups
LEFT JOIN
(SELECT MAX(added), group_id, name FROM link LEFT JOIN users ON users.id = link.user_id GROUP BY user_id) l
ON l.group_id = user_groups.id
GROUP BY user_groups.id
My question is if the query I have written could be optimized, or written better.
Thanks!
Ben
You actual query is not giving you the answer you want; at least, as far as I understand your question. John actually joined group 2 on 2017-01-05, yet it appears on group 1 (that he joined on 2017-01-01) on your results. Note also you're missing one Group 4.
Using standard SQL, I think the next query is what you're looking for. The comments in the query should clarify what each part is doing:
SELECT
user_groups.name AS group_name,
COUNT(u.name) AS member_count,
group_concat(u.name separator ', ') AS members
FROM
user_groups
LEFT JOIN
(
SELECT * FROM
(-- For each user, find most recent date s/he got into a group
SELECT
user_id AS the_user_id, MAX(added) AS last_added
FROM
link
GROUP BY
the_user_id
) AS u_a
-- Join back to the link table, so that the `group_id` can be retrieved
JOIN link l2 ON l2.user_id = u_a.the_user_id AND l2.added = u_a.last_added
) AS most_recent_group ON most_recent_group.group_id = user_groups.id
-- And get the users...
LEFT JOIN users u ON u.id = most_recent_group.the_user_id
GROUP BY
user_groups.id, user_groups.name
ORDER BY
user_groups.name ;
This can be written in a more compact way in MySQL (abusing the fact that, in older versions of MySQL, it doesn't follow the SQL standard for the GROUP BY restrictions).
That's what you'll get:
group_name | member_count | members
:--------- | -----------: | :-------------
Group 1 | 2 | Mikie, Dominic
Group 2 | 2 | John, Paddy
Group 3 | 0 | null
Group 4 | 1 | Nellie
dbfiddle here
Note that this query can be simplified if you use a database with window functions (such as MariaDB 10.2). Then, you can use:
SELECT
user_groups.name AS group_name,
COUNT(u.name) AS member_count,
group_concat(u.name separator ', ') AS members
FROM
user_groups
LEFT JOIN
(
SELECT
user_id AS the_user_id,
last_value(group_id) OVER (PARTITION BY user_id ORDER BY added) AS group_id
FROM
link
GROUP BY
user_id
) AS most_recent_group ON most_recent_group.group_id = user_groups.id
-- And get the users...
LEFT JOIN users u ON u.id = most_recent_group.the_user_id
GROUP BY
user_groups.id, user_groups.name
ORDER BY
user_groups.name ;
dbfiddle here

mysql search with two joins and a count

I am currently trying to write a general query which returns the content of 1 table and another joined table plus the count of resulting rows from a third table.
Now my description might seem abstract so I'll try to visualize it
Tables:
posts
| ID | title | description | creator_id |
1 Title1 Descr1 1
2 Title2 Descr2 1
users
| ID | name | avatar |
1 User1 PATH
interactions
| ID | type | target_id | identifier |
1 view 1 IP
2 view 1 IP
Now what I am looking for is an output like this:
| ID | title | description | name | avatar | view_count |
1 Title1 Descr1 User1 PATH 2
2 Title2 Descr2 User1 PATH 0
My current query looks like following:
SELECT
posts.id, posts.title, posts.description,
users.name, users.avatar,
COUNT(interactions.id) AS view_count
FROM
posts
LEFT JOIN
users
ON
posts.creator_id = users.id
LEFT JOIN
interactions
ON
posts.id = interactions.target_id
But only prints out the posts result which has an interaction like this:
| ID | title | description | name | avatar | view_count |
1 Title1 Descr1 User1 PATH 2
How do I need to alter the query in order to also get the other rows which happen to not have any interactions yet?
Thank you for your help!
You can simply subquery third table to count entries:
SELECT
posts.id, posts.title, posts.description,
users.name, users.avatar,
(SELECT COUNT(*) FROM interactions i WHERE i.target_id = posts.id) AS view_count
FROM
posts
LEFT JOIN
users
ON
posts.creator_id = users.id
This is also better for performance (no groups, no unoptimized joins)
Try this:
SELECT P.ID
, P.title
, P.description
, U.name
, U.avatar
, IFNULL(COUNT(I.ID), 0) AS view_count
FROM posts P
LEFT JOIN users U ON U.ID = P.creator_id
LEFT JOIN interactions I ON I.target_id = P.ID
GROUP BY P.ID
It seems like you missed the GROUP BY clause. Without this, when you use an aggregate function like COUNT, the documentation says:
there is a single group and it is indeterminate
which name value to choose for the group
That's why your query only returned 1 row.
Try this;)
select posts.id, posts.title, posts.description, users.name, users.avatar, coalesce(t3.view_count, 0) as view_count
from posts
left join users on posts.creator_id = users.id
left join (
select target_id, count(1) as view_count from interactions group by target_id
) t3 on posts.id = t3.target_id
SQLFiddle HERE

MYSQL Optimising JOINED UNION SELECT query

I've re-written this query a few times now (it's Monday) with the attempt of finding the most efficient way of getting the data I require however I'm not sure I'm even approaching it correctly at the moment.
To summarise the problem;
Users have two sets of tags (key_terms, project_terms), there's a link table between each of these between users and tags tables.
I would like to pull out any users that have specified tags in either table. Ideally it'd also include the 'most relevant' tag to that user - but lets put that aside for now.
users
| id | name |
| 1 | dayjo |
| 2 | stackoverflow |
tags
| id | tag |
| 1 | tag1 |
| 2 | tag2 |
user_key_term
| user_id | tag_id |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
project_key_term
| user_id | tag_id |
| 1 | 3 |
| 2 | 3 |
What I want to be able to query on is the named tags, i.e if I search for "tag1" both users should be returned, however if I search for "tag2" only User 1 should be returned.
My Solutions
I tried by selecting users, and joining tags twice (one for each link table), this seemed to work ok but wasn't sure it was the best way, and couldn't figure out how to get the most relevant tag.
SELECT t1.tag, t2.tag as most_relevant_tag, users.* FROM users
LEFT JOIN user_key_term ON user_key_term.user_id = users.id
LEFT JOIN tags t1 ON user_key_term.tag_id = t1.id
LEFT JOIN project_key_term ON project_key_term.user_id = users.id
LEFT JOIN tags t2 ON project_key_term.tag_id = t2.id
WHERE t1.tag IN ('tag1','tag2') OR t2.tag IN ('tag1','tag2')
GROUP BY users.id;
My next attempt was a UNION select, but this one feels dirty;
SELECT users.* FROM
`users`
INNER JOIN (
SELECT project_key_term.user_id, tags.id, tags.tag FROM project_key_term
JOIN tags ON tags.id = project_key_term.tag_id AND tags.tag IN ('tag1')
UNION ALL
SELECT user_key_term.user_id,tags.id, tags.tag FROM user_key_term
JOIN tags ON tags.id = user_key_term.tag_id AND tags.tag IN ('tag1')
) tags ON tags.user_id = users.id
WHERE tags.tag IN ('tag1')
GROUP BY users.id;
But
I've tried running EXPLAIN on both queries to see which is best, but it doesn't reveal anything particularly useful to me. Especially because at the moment there's not a lot of data in the tables, there will potentially be hundreds / thousands of tags.
Any help on the 'correct' or best practice way to do this sort of query would be great!
The union query can be simplified to:
SELECT users.*
FROM users
INNER JOIN (
SELECT user_id,tag_id
FROM project_key_term
UNION ALL
SELECT user_id,tag_id
FROM user_key_term
) alltags ON alltags.user_id = users.id
INNER JOIN tags t on t.id = alltags.tag_id
where t.tag IN ('tag1')
Edit: Getting the most relevant tags
SELECT score, t.tag, users.*
FROM users
INNER JOIN (select user_id, tag_id, count(*) as score
from (SELECT user_id,tag_id
FROM project_key_term
UNION ALL
SELECT user_id,tag_id
FROM user_key_term
) alltags
group by user_id,tag_id) tagcounts ON tagcounts.user_id = users.id
INNER JOIN tags t on t.id = tagcounts.tag_id
where t.tag IN ('tag1','tag2','tag3')
ORDER BY score DESC

MySQL joining table's maximum row contents

For a simple forum I would like to list the threads in order from most recent post. That I have completed with no issue with the following query:
SELECT t.thread_id, t.title, MAX(m.post_id) as latest_reply
FROM forum_thread as t
LEFT JOIN forum_post as n ON(latest_reply = m.thread_id)
WHERE t.forum_id=:forum_id AND t.sticky=0
GROUP BY t.thread_id
ORDER BY latest_reply DESC
LIMIT :limit_bottom, :limit_top
This works fine, until I want more details from the maximum post row. If I select more info from the post table, the results are random
I would like to also know, the post title, and then the username who posted.
The thread table [forum_thread] looks like the following:
thread_id | forum_id | title | sticky | post_count | view_count | closed
The post table [forum_post] looks like the following:
post_id | user_id | thread_id | timestamp | title | post_body_cache
And the user table [user] looks like the following:
id | username
I need to join the maximum post row to get the title, and than join the user table to get the username. What is the most efficient way of doing this? Everything I have tried has returned a fatal error.
Desired results would be:
[forum_thread] thread_id | [forum_thread] title | [forum_post] MAX post_id | [forum_post] title | [user] username
To get the entire row from the outer table, you have to move the Max to a subquery and use it in the joining criteria:
SELECT t.thread_id, t.title, p.author, p.post_date, p.whatever
FROM forum_thread as t
LEFT JOIN forum_post as p
ON p.thread_id = m.thread_id
and p.post_date =(
select Max( p.post_date )
from forum_post
where thread_id = p.thread_id )
WHERE t.forum_id = :forum_id
AND t.sticky= 0

Duplicate content in LEFT JOIN query + get count on joined table

I am trying to join two tables with similar ids, then get a sum of two fields as well. let me explain:
test table: id | post | desc | Date
likes_dislikes table: id | song_id | user_ip | like | dislike
on test 'test table', the 'id' matches that of the likes_dislikes 'song_id', so I tried LEFT JOIN since not every post will have an id in the likes_dislikes table, but I got duplicate results .
SELECT *
FROM
test
LEFT JOIN likes_dislikes ON test.song_id = likes_dislikes.page_id
GROUP BY test.song_id
ORDER BY test.id DESC LIMIT $start, $limit
how can I prevent the duplicate content, and also, get the TOTAL likes/dislikes associated with each post as I run through a while loop?
I Assume you are looking for something like this:
SELECT
T.`id`,
T.`post`,
T.`desc`,
T.`Date`,
COUNT(L.`like`) as `LikeCount`,
COUNT(L.`dislike`) as `DislikeCount`
FROM `test` T
LEFT JOIN `likes_dislikes` L
ON T.`Id` = L.`song_id`
GROUP BY T.`Id`, T.`post`, T.`desc`, T.`Date`
ORDER BY T.`id` DESC;