Get latest entry and join another table - mysql

I'm trying to get the username and timestamp of the most recent post where topicID is, for example, 88.
Users
id | username
--------|----------
45234 | kaka
32663 | lenny
52366 | bob
Posts
id | message | topicID | timestamp | userID
--------|-----------|---------|-----------|-------
675 | hello | 88 | 100 | 32663
676 | hey | 88 | 200 | 45234
677 | howdy | 88 | 300 | 52366
So here I would want postID 677 and user bob.
Can I do this in a single sql query?
Would be great if I could implent it into this:
SELECT topics.id, topics.subject, topics.forum_id
FROM topics WHERE topics.forumID = 16

Assuming that table Topic is linked with table Post by Topic.ID = Post.TopicID and you want to get the latest post associated with it, you can have a subquery which basically gets the latest id (assuming it's set as auto-incremented column) for each topicID and join the result on table Post to get the other columns. Also you need to join on table User in order to get the name of the user who posted the entry.
SELECT a.id,
a.subject,
a.forumid,
b.message,
b.timestamp,
d.username
FROM topic a
INNER JOIN Posts b
ON a.id = b.topicID
INNER JOIN
(
SELECT topicID, MAX(id) id
FROM Posts
GROUP BY topicID
) c ON b.topicID = c.topicID AND
b.id = c.ID
INNER JOIN users d
ON b.userID = d.id
WHERE a.forumID = 16
if you remove the WHERE clause, you will get all the latest entry for each forumID.
SQLFiddle Demo

Untested, but off the top of my head, I think the following query will get you what you want:
SELECT Users.username, Posts.timestamp
FROM Users JOIN Posts on Users.id = Posts.userID
WHERE Posts.topicID = 88
ORDER BY Posts.timestamp DESC
LIMIT 1

Related

Mysql select unique field from a groupped table

I have table named as posts
|id|user_id|title|description|
likes table like this
|id|user_id|post_id|
here is my sql code
select count(likes.id) as like_count, posts.* from `posts` left join
`likes` on `likes`.`post_id` = `posts`.`id` where `posts`.`status` = 'A'
group by `posts`.`id` order by `like_count` desc LIMIT 6
Here is the result
|like_count | id | user_id | title | description |
---------------------------------------------------
17 | 723 | 12 | ... | .... |
15 | 721 | 15 | ... | .... |
14 | 711 | 12 | ... | .... |
13 | 700 | 12 | ... | .... |
12 | 800 | 11 | ... | .... |
12 | 920 | 12 | ... | .... |
as you see user_id 12 is repeating thee times, I want only one post from one user.
Is there a way to select most liked post of each user, and make user_id don't repeat.
I'm aware this may not be the most efficient solution, but this is the first that came to my mind. Unfortunately, MySQL doesn't support window functions so the task is way harder and less elegant than the approach one would take using window functions.
Most liked posts of each user which will repeat user_id if there happens to be more than one post with equal number of likes and that number is the greatest for particular user:
select
user_likes.post_user_id,
user_likes.like_count,
p.id as post_id,
p.title,
p.description
from (
select
p.user_id as post_user_id,
MAX(l.like_count) as like_count
from posts p
inner join (select post_id, count(*) as like_count from likes group by post_id) l on p.id = l.post_id
group by p.user_id
) user_likes
inner join (select post_id, count(*) as like_count from likes group by post_id) l on user_likes.like_count = l.like_count
inner join posts p on l.post_id = p.id
If you really need one row per user then in case of ties in number of likes for different posts you need to pick a strategy to retrieve only one of those. For example you could take the one that was older by adding grouping on post_id and taking the min() of its value and joining back to posts table to retrieve it's title and description.
In this case modification of above script would look like:
select
most_liked_post.post_user_id,
most_liked_post.like_count,
most_liked_post.post_id,
p.title,
p.description
from (
select
user_likes.post_user_id,
user_likes.like_count,
MIN(l.post_id) AS post_id
from (
select
p.user_id as post_user_id,
MAX(l.like_count) as like_count
from posts p
inner join (select post_id, count(*) as like_count from likes group by post_id) l on p.id = l.post_id
group by p.user_id
) user_likes
inner join (select post_id, count(*) as like_count from likes group by post_id) l on user_likes.like_count = l.like_count
group by user_likes.post_user_id, user_likes.like_count
) most_liked_post
inner join posts p on most_liked_post.post_id = p.id

Oh MySQL, how doth thou join related records by date

I have what can only be described as a seemingly simple problem with most likely a simple solution; yet that simple solution escapes me. I've searched and trudged through the vast web of StackOverflow only to come up short finding only solutions that seem to be extremely complex. I've already kind of solved this problem, but, it's disgusting and I'm ashamed of it. Surely there is a better way, for there must be a Knight in shining MySQL armor wielding a query sword who can come up with a simple and elegant solution. Here goes:
For the sake of this question, we'll keep the two tables simple.
Table 1
users (user_id, active, name)
and
Table 2
user_projects (user_project_id, user_id, start_date, details)
In this case, records for users are added as needed. Related records are then added as projects are completed by users.
My question is: Using a query, how can I get 1 record containing all of the information in table 1 active users joined with the record from table 2 with the most recent date based on start_date?
In other words, if I have this:
| user_id | active | name |
| 1 | 1 | brian |
and this:
| user_project_id | user_id | start_date | details |
| 1 | 1 | 2013-10-02 | proj 1 |
| 2 | 1 | 2013-11-26 | proj 2 |
| 3 | 1 | 2014-01-02 | proj 3 |
produce the query that gives me this:
| user_id | active | name | user_project_id | user_id | start_date | details |
| 1 | 1 | brian | 3 | 1 | 2014-01-02 | proj 3 |
Oh please oh please let there be an answer for I will surely wither without one.
Since in MySQL there is not such things as top selectors, you can use triple JOIN, like:
SELECT
user_projects.*,
users.*
FROM
(SELECT
MAX(start_date) AS max_date,
user_id
FROM
user_projects
GROUP BY
user_id) AS max_dates
LEFT JOIN
user_projects
ON max_dates.max_date=user_projects.start_date
AND max_dates.user_id=user_projects.user_id
LEFT JOIN
users
ON users_projects.user_id=users.user_id
First select the projects per user that are the most recent:
SELECT a.*
FROM user_projects a
JOIN (
SELECT user_id, MAX(start_date) AS max_start_date
FROM user_projects
GROUP BY user_id
) b ON a.user_id = b.user_id AND a.start_date = b.max_start_date
It creates a small helper table from user_projects comprising the user and for each row their most recent project date; to get all the corresponding table fields you must join that with user_projects again.
Then, you simply join users with the above outcome to get the final result:
SELECT *
FROM users
JOIN (
SELECT a.*
FROM user_projects a
JOIN (
SELECT user_id, MAX(start_date) AS max_start_date
FROM user_projects
GROUP BY user_id
) b ON a.user_id = b.user_id AND a.start_date = b.max_start_date
) c ON users.user_id = c.user_id
select u1.user_id, u1.name, u.strat_date, u.user_project_id, u.details
from user_projects u
left outer join users u1 on u1.user_id=u.user_id
left outer join
(select user_id, max(strat_date) as strat_date user_projects group by user_id) as A
on A.user_id=u.user_id and A.strat_date=u.strat_date
You can use top 1 and order by date
select top 1 a.user_id,a.name,b.user_project_id,b.user_id,b.start_date,b.details
from users a join users_project b on a.user_id = b.user_id
order by b.start_date desc
Sorry i didnt notice mysql tag in OP's question. You can use Limit keyword.
select a.user_id,a.name,b.user_project_id,b.user_id,b.start_date,b.details
from users a join users_project b on a.user_id = b.user_id
order by b.start_date desc Limit 1

Strange order of results when adding joins

I'm trying to build a commenting system on my website but having issues with ordering the comments correctly. This is a screenshot of what I had before it went wrong:
And this is the query before it went wrong:
SELECT
com.comment_id,
com.parent_id,
com.is_reply,
com.user_id,
com.comment,
com.posted,
usr.username
FROM
blog_comments AS com
LEFT JOIN
users AS usr ON com.user_id = usr.user_id
WHERE
com.article_id = :article_id AND com.moderated = 1 AND com.status = 1
ORDER BY
com.parent_id DESC;
I now want to include each comment's votes from my blog_comment_votes table, using a LEFT OUTER JOIN, and came up with this query, which works, but screws with the order of results:
SELECT
com.comment_id,
com.parent_id,
com.is_reply,
com.user_id,
com.comment,
com.posted,
usr.username,
IFNULL(c.cnt,0) votes
FROM
blog_comments AS com
LEFT JOIN
users AS usr ON com.user_id = usr.user_id
LEFT OUTER JOIN (
SELECT comment_id, COUNT(vote_id) as cnt
FROM blog_comment_votes
GROUP BY comment_id) c
ON com.comment_id = c.comment_id
WHERE
com.article_id = :article_id AND com.moderated = 1 AND com.status = 1
ORDER BY
com.parent_id DESC;
I now get this order, which is bizarre:
I tried adding a GROUP BY clause on com.comment_id but that failed too. I can't understand how adding a simple join can alter the order of results! Can anybody help back on the correct path?
EXAMPLE TABLE DATA AND EXPECTED RESULTS
These are my relevant tables with example data:
[users]
user_id | username
--------|-----------------
1 | PaparazzoKid
[blog_comments]
comment_id | parent_id | is_reply | article_id | user_id | comment
-----------|-----------|----------|------------|---------|---------------------------
1 | 1 | | 1 | 1 | First comment
2 | 2 | 1 | 1 | 20 | Reply to first comment
3 | 3 | | 1 | 391 | Second comment
[blog_comment_votes]
vote_id | comment_id | article_id | user_id
--------|------------|------------|--------------
1 | 2 | 1 | 233
2 | 2 | 1 | 122
So the order should be
First comment
Reply to first comment +2
Second Comment
It's difficult to say without looking at your query results, but my guess is that it's because you are only ordering by parent id and not saying how to order when two records have the same parent id. Try changing your query to look like this:
SELECT
com.comment_id,
com.parent_id,
com.is_reply,
com.user_id,
com.comment,
com.posted,
usr.username,
COUNT(c.votes) votes
FROM
blog_comments AS com
LEFT JOIN
users AS usr ON com.user_id = usr.user_id
LEFT JOIN
blog_comment_votes c ON com.comment_id = c.comment_id
WHERE
com.article_id = :article_id AND com.moderated = 1 AND com.status = 1
GROUP BY
com.comment_id,
com.parent_id,
com.is_reply,
com.user_id,
com.comment,
com.posted,
usr.username
ORDER BY
com.parent_id DESC, com.comment_id;

show results with biggest count mysql

I need to show user with the most comments. I have two tables:
Table: Users
ID | USERNAME | EMAIL
------------------------------
1 | USER01 | EMAIL01
2 | USER02 | EMAIL02
3 | USER03 | EMAIL03
4 | USER04 | EMAIL04
Table: Comments
ID | AUTHOR | COMMENT
----------------------------------
1 | USER01 | COMMENT...
2 | USER02 | COMMENT...
3 | USER01 | COMMENT...
4 | USER03 | COMMENT...
In this example the user01 have the most comments, but lets say I have to result them all with count of comments they have. And also in result I have to show users email which is stored in Users table.
How can I count and at same time check in both tables to return result? Or should I first get user info and then count ?
this query below handles duplicate rows having the most number of comments,
SELECT a.userName
FROM Users a
INNER JOIN Comments b
ON a.username = b.author
GROUP BY a.userName
HAVING COUNT(*) =
(
SELECT MAX(totalCount)
FROM
(
SELECT author, COUNT(*) totalCount
FROM comments
GROUP BY author
) a
)
SQLFiddle Demo
SQLFiddle Demo (with duplicate)
but if you want not to handle that, it can be simply done by using ORDER BY and LIMIT
SELECT a.userName, COUNT(*) totalCount
FROM Users a
INNER JOIN Comments b
ON a.username = b.author
GROUP BY a.userName
ORDER BY totalCount DESC
LIMIT 1
SQLFiddle Demo
select username,email,count(*) as cnt
from users, comments
where author = username
group by username
order by cnt desc
limit 1

How can I filter an SQL query with a GROUP BY clause

I'm trying to formulate an SQL query for a messaging system that will return a list of all threads which have a status of 'OPEN' and whose last message was not posted by a user in a certain group.
The tables involved in the query are:
threads
-------
threadId
timestamp
subject
status
clientId
messages
--------
messageId
userId
messagebody
timestamp
threadId
users
-----
userId
username
groupId
The current query is:
SELECT threadId, timestamp, subject, status, clientId
FROM threads
WHERE status='OPEN'
ORDER BY timestamp DESC
which works fine; but I now have a new requirement - if the last message in the thread was posted by a user with a groupId of 1 then it should NOT be in the result set.
Including the information I need is simple enough:
SELECT threadId, timestamp, subject, status, clientId
FROM threads
INNER JOIN messages USING(threadId)
INNER JOIN users USING(userId)
WHERE status='OPEN'
GROUP BY threadId
ORDER BY timestamp DESC
but at this point I get stuck, because I can't figure out how to get the query to filter out threads based on the messages.userId with the highest timestamp within a particular group. I've looked at HAVING and all the aggregate functions and nothing seems to fit the bill.
Edit
I may not have been clear enough, so I'll try to illustrate with an example.
Here's some example data:
threads
threadId | timestamp | subject | status | clientId
--------------------------------------------------
1 | 1 | One | OPEN | 1
2 | 10 | Two | OPEN | 4
3 | 26 | Three | OPEN | 19
4 | 198 | Four | OPEN | 100
messages
messageId | userId | messagebody | timestamp | threadId
-------------------------------------------------------
1 | 3 | Hello, World| 1 | 1
2 | 1 | Hello | 3 | 1
3 | 1 | ^&%&6 | 10 | 2
4 | 2 | Test | 12 | 2
5 | 4 | Hi mum | 26 | 3
6 | 1 | More tests | 100 | 4
users
userId | username | groupId
---------------------------
1 | Gareth | 1
2 | JimBob | 2
3 | BillyBob | 2
4 | Bod | 3
In this example dataset threads 1 and 4 should be eliminated from the query because user Gareth (a member of group 1) is the last one to post to them (messageId 2 in threadId 1, and messageId 5 in threadId 4). So the result set should only have the thread data (threadId, timestamp, subject, status and clientId) for threads 2 and 3.
I've created an SQLfiddle for this test data.
Can anyone point me in the right direction here?
Sounds like you want this:
SELECT threadId, timestamp, subject, status, clientId
FROM threads t
INNER JOIN messages m
ON t.threadId = m.threadId
INNER JOIN users u
ON m.userId = u.userId
and u.groupId != 1 --placed the groupId filter here.
WHERE status='OPEN'
GROUP BY threadId
ORDER BY timestamp DESC
Edit, this appears to give you what you need:
SELECT t.threadId,
t.timestamp,
t.subject,
t.status,
t.clientId
FROM threads t
INNER JOIN messages m
ON t.threadId = m.threadId
INNER JOIN users u
ON m.userId = u.userId
WHERE status='OPEN'
AND NOT EXISTS (select t1.threadid
FROM threads t1
INNER JOIN messages m
ON t1.threadId = m.threadId
INNER JOIN users u
ON m.userId = u.userId
where u.groupid = 1
and t.threadid = t1.threadid)
ORDER BY timestamp DESC
see SQL Fiddle with Demo
Does adding LIMIT 1 to your query solve your problem?
SELECT threadId, timestamp, subject, status, clientId
FROM threads
INNER JOIN messages USING(threadId)
INNER JOIN users USING(userId)
WHERE status='OPEN'
GROUP BY threadId
ORDER BY timestamp DESC
LIMIT 1
Or changing the where clause to
WHERE status='OPEN' and groupID<>1
I think this is what you want?
select * from threads
where threadId not in
(
select messages.threadId
from messages
inner join (select threadId, MAX(timestamp) maxtime from messages group by threadId) t
on messages.threadId = t.threadId
and messages.timestamp = t.maxtime
where messages.userId=1
)