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

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;

Related

Order by last row of SELECT subquery

I have 2 tables: "posts" - for posts that a user post, and all the information it includes. and "messages" - which includes messages for each post.
The tables look like this:
posts:
--------------------------------------------------
| post_id | post_creator_id | post_information |
--------------------------------------------------
messages:
----------------------------------------------------------
| message_id | post_id | user_id | message_text |
----------------------------------------------------------
I want to display a user (user_id) a list of all chats he is engaged in, and order it by the last message sent in any chat.
So there are a few things to be done: first I need to group by the post_id in messages table (so there aren't duplicates) and order by message_id DESC, I tried subquery and joins but neither worked. Here is an example of my subquery attempt:
SELECT posts.post_information FROM posts WHERE posts.post_id in (SELECT
messages.post_id FROM messages ORDER BY message_id DESC)
Can't figure out how make the query display only distinct rows (without duplication of post_id) And whether join is a better option?
Simple change in your query, we can use INNER JOIN with messages table and ORDER BY with message_id instead of using suqbyery (It seems that you want to order output data by message_id in descending order.
SELECT posts.post_information
FROM posts
INNER JOIN (SELECT post_id, max(message_id) mid
FROM messages
GROUP BY post_id) mes ON mes.post_id = posts.post_id
ORDER BY mes.mid DESC
Assuming you have multiple messages per post, you need to use distinct not to include duplicates. Than just join tables and add order by
SELECT distinct posts.post_information
FROM posts, messages where messages.post_id = posts.post_id
ORDER BY messages.message_id DESC

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 - Fetching users who sent the most recent messages

I have a messages table as follows:
Basically what I want is, I want to fetch n users who sent the most recent messages to a group. So it has to be grouped by from_user_id and sorted by id in descending order. I have the following query:
SELECT `users`.`id` AS `user_id`, `users`.`username`, `users`.`image`
FROM `group_messages`
JOIN `users` ON `users`.`id` = `group_messages`.`from_user_id`
WHERE `group_messages`.`to_group_id` = 31
GROUP BY `users`.`id`
ORDER BY `group_messages`.`id` DESC;
The problem with this is, when I group by user.id, the row with the smallest id field is taken into account. Therefor what I get is not in the order which id is descending.
So is there a way to group by, taking the greatest id into account ? Or should I approach it another way ?
Thanks in advance.
Edit: I think I got it.
SELECT `x`.`id`, `users`.`id` AS `user_id`, `users`.`username`, `users`.`image`
FROM (SELECT * FROM `group_messages` ORDER BY `group_messages`.`id` DESC) `x`
JOIN `users` ON `users`.`id` = `x`.`from_user_id`
WHERE `x`.`to_group_id` = 31
GROUP BY `users`.`id`
ORDER BY `x`.`id` DESC;
Just had to make a select from an already ordered list.
Using order by inside subquery is not a very efficient way. Try this:
users table
| uid | name | ---------- |
messages table
|id | from_uid | to_group_id | ----------- |
SELECT u.* FROM users as u JOIN (
SELECT g1.*
FROM messages g1 LEFT JOIN messages g2
ON g1.from_uid = g2.from_uid AND g1.to_groupid = g2.to_groupid
AND g1.id<g2.id
WHERE g2.id is NULL) as lastmessage
ON lastmessage.from_uid = u.uid
WHERE lastmessage.to_groupid = 1
ORDER BY lastmessage.id DESC;
Its not good to have order by in subqueries because they will make the queries slow and in your case u were running it on the whole table .
Check this out Retrieving the last record in each group.

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

Count not giving right results with three joins

I have three tables (MySQL)
forum: each line in this table is a comment in the forum related to the match by static_id and related to the author by user_id
|match_static_id| date | time | comments | user_id |
matches: this table contains matches with all its information
| static_id | localteam_name | visitorteam_name | date | time |.......
iddaa : this table contains a code for each match (some matches do not have codes here)
|match_static_id| iddaa_code |
I make a query like following:
SELECT forum.match_static_id, forum.date, forum.time,
count(forum.comments) 'comments_no', matches.*, users.username, iddaa.iddaa_code
FROM forum
INNER JOIN matches ON forum.match_static_id = matches.static_id
INNER JOIN users on forum.user_id = users.id
LEFT JOIN iddaa on forum.match_static_id = iddaa.match_static_id
GROUP BY forum.match_static_id
ORDER BY forum.date DESC, forum.time DESC
the query work as I want (I get the match information, iddaa code for the match if there is one, and the author of the comment(last comment) ).
The problem is in the "count function" I should get the number of the comments related to the same match bur the query returned (double of each value)
for example if I have 5 comments for a match it returns 10
I want to know if all parts of my query is right and any help will be good?
Maybe it can be wrapped in a sub query? Its hard when i dont have the table def + data.
SELECT Sub.*, COUNT(1) 'comments_no'
FROM
(
SELECT forum.match_static_id, forum.date, forum.time,
matches.*, users.username, iddaa.iddaa_code
FROM forum
INNER JOIN matches ON forum.match_static_id = matches.static_id
INNER JOIN users on forum.user_id = users.id
GROUP BY forum.match_static_id
) Sub
LEFT JOIN iddaa on Sub.match_static_id = iddaa.match_static_id
ORDER BY forum.date DESC, forum.time DESC