Mysql select unique field from a groupped table - mysql

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

Related

mysql joining four table and count results order by desc

I'm working on Mysql to get the trending posts
I am joining four tables posts, posts_like, posts_shares, posts_comments and distinct the user_id of it. (post like, shares and comments should be added for total)
I am expecting a result of this for example only
RESULT:
post_id | user_id | count of shares, like, comments
---------------------------------------------------
1 | 2 | 50 |
8 | 5 | 47 |
---------------------------------------------------
table structure
table users
---------------------
id | username | etc |
---------------------
table posts
-------------------------------------
id | user_id | content | datecreated
-------------------------------------
post_likes
--------------------------------
id | post_id | user_id |
--------------------------------
post_shares
--------------------------------
id | post_id | user_id |
--------------------------------
comments
---------------------------------------
id | post_id | user_id | description |
--------------------------------------
user_id should be distinct
You could use left join and count distinct based group by p.id, p.user_id, u.username
select p.id as post_id, p.user_id, u.username
, count(distinct l.user_id)
, count(distinct s.user_id)
, count(distinct c.user_id)
, ifnull(count(distinct l.user_id),0) +
ifnull(count(distinct s.user_id),0) +
ifnull(count(distinct c.user_id),0) total
from post p
inner join users u on u.id = p.user_id
left join post_likes l on l.post_id = p.id
left join post_shares s on s.post_id = p.id
left join comments c on c.post_id = p.id
group by p.id, p.user_id, u.username
order by total desc
To get count of shares, like, comments, I suggest you to use UNION over these three tables.
Something like (check the syntax):
SELECT post_id, user_id, count(post_id) From (
SELECT post_id, user_id FROM post_likes
UNION
SELECT post_id, user_id FROM post_shares
UNION
SELECT post_id, user_id FROM comments) Group by post_id, user_id

How to mysql distinct one of many count fields when GROUP BY doesn't work?

I've a database with products, manufactors and categories of this products and information about name of manufactors, products and if those products have images.
But in this database are duplicated products with different IDs. Only thing I can identify them is their name.
Now I've a query where I want see how many products per manufactorer and per category were in my database and count also the number of products with images.
-----------------------------------------------------
| manufactor | category | products | productsImages |
|------------|----------|----------|----------------|
| manu-1 | cat-1 | 5 | 3 |
|------------|----------|----------|----------------|
| manu-1 | cat-2 | 15 | 8 |
|------------|----------|----------|----------------|
| manu-2 | cat-1 | 11 | 0 |
|------------|----------|----------|----------------|
| manu-3 | cat-2 | 5 | 4 |
|------------|----------|----------|----------------|
| manu-3 | cat-3 | 9 | 4 |
|------------|----------|----------|----------------|
My approach looks like:
SELECT m.`name` AS manufactor,
pg.`name` AS category,
COUNT(p.`name`) AS products,
COUNT(pi.`idImage`) AS productsImages
FROM `product` AS p
LEFT JOIN `product_image` AS pi ON pi.`idProduct` = p.`id`
INNER JOIN `manufacturer` AS m ON m.`id` = p.`idManufacturer`
INNER JOIN `product_groupname` AS pg ON pg.`id` = p.`idProductGroup`
GROUP BY p.`idManufacturer`, p.`idProductGroup`
ORDER BY m.`name`, p.`idProductGroup`;
I can't group by p.name because then I'd get a result row for each product.
Do COUNT(DISTINCT(p.name)) didn't help either.
So any suggestions or will I have do to subqueries?
If you want to count the distinct names where pi.`idImage` IS NOT NULL you can use the following:
SELECT m.`name` AS manufactor,
pg.`name` AS category,
COUNT(DISTINCT p.`name`) AS products,
COUNT(DISTINCT CASE
WHEN pi.`idImage` IS NOT NULL THEN p.`name`
ELSE NULL
END) AS productsImages
FROM `product` AS p
LEFT JOIN `product_image` AS pi ON pi.`idProduct` = p.`id`
INNER JOIN `manufacturer` AS m ON m.`id` = p.`idManufacturer`
INNER JOIN `product_groupname` AS pg ON pg.`id` = p.`idProductGroup`
GROUP BY p.`idManufacturer`, p.`idProductGroup`
ORDER BY m.`name`, p.`idProductGroup`;
See comments on question - distinct on p.name is the solution.
SELECT m.`name` AS manufactor,
pg.`name` AS category,
COUNT(DISTINCT(p.`name`)) AS products,
COUNT(pi.`idImage`) AS productsImages
FROM `product` AS p
LEFT JOIN `product_image` AS pi ON pi.`idProduct` = p.`id`
INNER JOIN `manufacturer` AS m ON m.`id` = p.`idManufacturer`
INNER JOIN `product_groupname` AS pg ON pg.`id` = p.`idProductGroup`
GROUP BY p.`idManufacturer`, p.`idProductGroup`
ORDER BY m.`name`, p.`idProductGroup`;

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;

Get latest entry and join another table

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

how to join 3 tables, topic, comment and user

I have 3 tables, tbl_topic, tbl_comment, tbl_user.
I want to select all the topics created by the user and also the topics that he commented on even if he is not the creator. Here is my database:
tbl_topic
----------
topic_id
topic_title
user_id
tbl_comment
----------
comment_id
comment_message
user_id
topic_id
tbl_user
----------
user_id
user_name
Need it so badly. Thanks!
So far i got this
select * from tbl_topic T inner join tbl_comment C on T.topic_id = C.topic_id inner join tbl_user U on T.user_id = U.user_id GROUP BY T.topic_id
My problem is it only returns the topics that has comments on it. I want to include the topics created by the user even if it has 0 comments.
I want the result to be like this:
+-----------+-----------+----------+-------------+----------------+----------+-------
| topic_id | topic_title | user_id | comment_id | comment_message | user_id | topic_id |
+-----------+-----------+----------+-------------+----------------+----------+--------
| 1 | my topic | 1 | 1 | comment me | 1 | 1
| 2 | others | 2 | 2 | comment me | 1 | 2
| 3 | my nocoment| 1 | NULL | NULL | NULL | NULL
+-----------+---------+--------+-------------+----------+----------+---------+--------
----------+-----------+
user_id | user_name |
-----------+-----------
1 | me |
2 | someone |
1 | me
-----------+---------+--
I messed up with my fields in my tables, the user_id beside comment_message should be comment_user_id but i already created my database that way. Can you help make this possible?
The query below uses UNION in the subquery. The first SELECT gets all topics created by user. The second SELECT statement gets all comments of the user and joins it to table tbl_topic so we can get the topic_title.
SELECT topic_ID, topic_title
FROM
(
SELECT a.user_ID, b.topic_ID, b.topic_title
FROM tbl_user a
INNER JOIN tbl_topic b
ON a.user_ID = b.user_ID
UNION
SELECT a.user_ID, c.topic_ID, c.topic_title
FROM tbl_user a
INNER JOIN tbl_comment b
ON a.user_ID = b.user_ID
INNER JOIN tbl_topic c
ON b.topic_ID = c.topic_ID
) x
WHERE x.user_ID = ?
Try the query below. This will show all the fields.
SELECT tt.*, tc.*, tbl_user.*
FROM tbl_topic AS tt INNER JOIN tbl_comment AS tc ON tt.topic_id = tc.topic_id INNER JOIN tbl_user as tc ON tc.user_id = tu.user_id;
WHERE tu.user_id = x
If you have to filter add to the WHERE clause to the query.
Go with a left join. But there is still a Problem left, you will only get ONE table-comment and ONE user. To get more, you can use the GROUP_CONCAT function, like here: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
select * from tbl_topic T
LEFT JOIN tbl_comment C on T.topic_id = C.topic_id
LEFT join tbl_user U on T.user_id = U.user_id
WHERE T.user_id = x
GROUP BY T.topic_id
EDIT: the where clause was missing