Order by joined table data - mysql

Here is my tables:
topics
topic_id name
1 "Help!"
2 "Hey!"
3 "What?"
posts
post_id topic date content
1 2 2016-05-01 "Hey there!"
2 1 2016-05-04 "How to use WIFI?"
3 1 2016-05-05 "I dont know"
4 1 2016-05-02 "What is WIFI?"
5 3 2016-05-06 "What what?"
6 2 2016-05-02 "Hello"
I have this code
SELECT * from topics
LEFT JOIN posts
ON posts.topic = topics.topic_id
I want to join the posts with last (most recent) record only, and sort the records from topics by posts.date,
but I don't how to.
Expected result:
topic_id post_id date ...
3 5 2016-05-06 ...
1 3 2016-05-05 ...
2 6 2016-05-02 ...

Please try this Query
SELECT * from topics LEFT JOIN posts ON posts.topic = topics.topic_id
Left join (Select posts.topics, Max(posts.date) as Date From posts Group by posts.topics) as postgroup
on posts.date = postgroup.date and posts.topic = postgroup.topics;

Let me assume that post_id defines the last record. You can do this in various ways. Here is a method using WHERE and a correlated subquery:
SELECT *
FROM topics t LEFT JOIN
posts p
ON p.topic = t.topic_id
WHERE p.post_id = (SELECT MAX(p2.post_id) FROM posts p2 WHERE p2.topic = p.topic)
ORDER BY p.post_date DESC;

Related

Joining three tables with 2 count function

I've been at it for a day and couldn't really figure it out. I'm making a page where users can post and other users can reply and like.
I have these three tables: posts, replies, and likes. FYI that they all have a different number of rows, see full details below.
Posts table
post_id message
------- -------
1 This is post #1
2 This is post #2
3 This is post #3
Replies table
reply_id post_id message
------- ------- -------
1 1 This is a reply to post #1
2 1 This is a reply to post #1
3 2 This is a reply to post #2
4 2 This is a reply to post #2
5 3 This is a reply to post #3
Likes table
like_id post_id liked
------- ------- -------
1 1 Yes
2 1 Yes
3 1 Yes
4 2 Yes
5 2 Yes
6 3 Yes
7 3 Yes
Those are the structure of my tables. What I need to achieve is like this below:
All tables joined and tallied
post_id total_replies total_likes
------- ------------- -----------
1 2 3
2 2 2
3 1 2
Basically, for the 1st post, it should show that it has 2 replies and 3 likes. I cannot seem to do it using two counts. It is giving me incorrect numbers.
select posts.post_id, count(replies.post_id) as total_replies, count(likes.post_id) as total_likes from posts
inner join replies on posts.post_id = replies.post_id
inner join likes on posts.post_id = likes.post_id
group by posts.post_id
I would suggest correlated subqueries:
select p.post_id,
(select count(*)
from replies r
where r.post_id = p.post_id
) as total_replies,
(select count(*)
from likes l
where l.post_id = p.post_id
) as total_likes
from posts p;
The problem with your query is that you are joining along two different dimensions, so you are getting a Cartesian product -- all likes and all replies for a given post.
Not only does this get rid of that problem, but with indexes on replies(post_id) and likes(post_id) this should have better performance than any solution that does an aggregation over all the data.
I took your query and made a small edit so that it produces desired output. However note that the query would not be efficient. Gordon’s solution is more effective in terms of efficiency.
select
posts.post_id,
count(distinct replies.reply_id) as total_replies,
count(distinct likes.like_id) as total_likes
from posts
inner join replies on posts.post_id = replies.post_id
inner join likes on posts.post_id = likes.post_id
group by
posts.post_id;
Or would be better to pre-aggregate your metrics. That way it would be bit more efficient.
select
posts.post_id,
r.total_replies,
L.total_likes
from posts
inner join (Select post_id, count(reply_id) as total_replies from replies group by post_id) r on posts.post_id = r.post_id
inner join (Select post_id, count(like_id) as total_likes from likes group by posts_id) L on posts.post_id = L.post_id;
The caveat is you won’t get post that weren’t liked or replied upon since you are doing inner join. To get all posts irrespective of likes or replies you have to do left join.

retrieve another users posts shared by followeds sql, mysql

I have this arrangement:
I want to retrieve all the posts shared by followeds, bearing in mind those shared posts doesnt belong to another followeds or the main follower.
TABLE USERS
id_user | name
15 Joe
16 Dan
17 Tom
TABLE COMMUNITY
id_follower | followed
15 16
15 17
TABLE POSTS
id_post | id_user | post
1 15 hi1
2 16 hi2
3 15 hi3
4 17 hi4
5 15 hi5
6 16 hi6
7 17 hi7
8 18 hi8
9 14 hi9
I'm adding up a new table "shares".
TABLE SHARES
id_sharer | id_post
16 4
14 8
17 8
16 9
EXPECTED OUTPUT POSTS
17 8 hi7 (shared by Tom)
16 9 hi6 (shared by Dan)
How shouldit be the sql query to get this new arrangement?
I was thinking adding a line LEFT JOIN shares l ON l.id_sharer=p.id_user
SELECT p.*,
FROM posts p
LEFT JOIN users u ON u.id_user=c.id_followed
LEFT JOIN shares l ON l.id_sharer=c.id_followed
LEFT JOIN community c ON c.id_follower=p.id_user
WHERE p.id_user=15
ORDER BY p.date_post DESC
*Tested but not retrieve as expected
Here is a first crack at this for you:
SELECT p.id_post, p.id_user, p.post, IF(l.date_like IS NOT NULL, l.date_like, p.date_post) AS date, l.id_liker
FROM posts p
LEFT JOIN likes l ON l.id_post = p.id_post
LEFT JOIN users u ON u.id_user = l.id_liker
ORDER BY l.date_like DESC
GROUP BY p.id_post
As a quick walk through of how I am approaching the problem, first I am LEFT JOINing the likes and ordering them in descending order, just as you did. Then I am grouping on the post id in order to ensure I only get one line per post. Then in the selector I am checking to see if the like date is null and substituting in the date of the post instead of the most recent like if there are no likes.
UPDATE
This should add the logic to ensure that only the likes of followers will count. Since one probably cannot follow themselves this should also keep one's own likes from affecting position.
SELECT p.id_post, p.id_user, p.post, IF(l.date_like IS NOT NULL, l.date_like, p.date_post) AS date, l.id_liker
FROM posts p
LEFT JOIN likes l ON l.id_post = p.id_post
LEFT JOIN users u ON u.id_user = l.id_liker
LEFT JOIN community c ON l.id_liker = c.followed
WHERE c.id_follower = 15
ORDER BY l.date_like DESC, p.date_post DESC
GROUP BY p.id_post
I understand that 'TABLE POSTS'.'id_user' is the author of the post and matches to 'TABLE USERS'.'id_user' and that you want to see the Posts that have Entries in 'TABLE LIKES' first and then all the others.
So I suggest to the following JOIN and ORDER BY
select p.*, l.date_like, u.* from posts p
left join likes l on p.id_post=l.id_post
left join users u on l.id_liker=u.id_user
order by l.date_like desc, p.date_post desc

Entry with most matching relations

I'm trying to create little "recommended" functionality based on the posts with the most matching tags.
I got a layout like this:
Posts
id
---
1
2
3
4
post_tags
post_id | tag_id
---------+---------
1 | 1
1 | 2
2 | 2
2 | 3
2 | 4
3 | 1
3 | 2
3 | 4
4 | 5
tags
id
----
1
2
3
4
5
So if I would retrieve recommendations for the post with id 1 the list should go
3 (2/2 matches)
2 (1/2 matches)
4 (0/2 matches)
My Query so far looks like this:
SELECT DISTINCT
p.id,
p.title,
count(*) as cnt
FROM
posts p
INNER JOIN posts_tags pt ON pt.post_id= p.id
INNER JOIN tags t ON pt.tag_id = t.id
WHERE
t.id IN (
SELECT
pt.tag_id
FROM
posts_tags pt
WHERE
pt.post_id = '30213'
)
GROUP BY
t. NAME
order by count(*) desc
LIMIT 0, 4
I know DISTINCT isn't working because of the count but I wanted to see just what he counted, so the result looks like this:
4 Foo 4881
4 Foo 2560
11 Bar 2094
12 Baz 1998
So what happened? It counted the occurences of the tag in general. So appearantly the first associated tag of "Post 1" is 4881 associated and then pulls the first entry that matches... the one with the lowest id.
I see the problem but I can't solve it.
Your group by makes no sense. You want to aggregate by the post not the tag:
SELECT p.id, p.title, count(*) as cnt
FROM posts p INNER JOIN
posts_tags pt
ON pt.post_id = p.id
WHERE pt.tag_id IN (SELECT pt2.tag_id
FROM posts_tags pt2
WHERE pt2.post_id = 30213
)
GROUP BY p.id, p.title
ORDER BY count(*) desc
LIMIT 0, 4;
This will not return 0. If that is important, you need to use a LEFT JOIN instead of WHERE . . . IN . . ..
Also:
SELECT DISTINCT is almost never used with GROUP BY. It is hard (but not impossible) to come up with a use-case for it.
You don't need the tags table, so I removed it.
Don't use single quotes around numbers. I am guessing that post_id is really a number.
The fix is in the GROUP BY.

Mysql select 4 tables with 3 count

I have four tables
users
ID display_name
1 Name1
2 Name2
3 Name3
A user can add books to table books
books
book_id AddedByuserID
1 1
2 1
3 2
4 3
Also a user can add ebooks to table ebooks
ebooks
ebook_id AddedByuserID
1 1
2 2
3 2
4 3
Now a user can add books only to his collection (not ebooks)
collection
userID book_id
1 1
1 2
1 3
I need an output like this:
display_name books_added ebooks_added books_in_collection
Name1 2 1 3
Name2 1 2 2
Name3 1 1 1
This is what got:
SELECT users.*, COUNT(DISTINCT collection.book_id) AS books_in_collection, COUNT(DISTINCT books.AddedByuserID) AS books_added, COUNT(DISTINCT ebooks.AddedByuserID) AS ebooks_added
FROM users LEFT JOIN collection ON users.ID = collection.userID
LEFT JOIN books ON users.ID = books.AddedByuserID
LEFT JOIN ebooks ON users.ID = ebooks.AddedByuserID
GROUP BY users.ID
ORDER BY display_name ASC
The user display_name gets displayed correct and also the collection count, but the two other counts are showing 1.
If I remove DISTINCT the collection count says 86, and the two other counts show nothing.
I try to understand LEFT join and read tutorials but i'm stuck at the moment.
I would suggest correlated subqueries:
select u.*,
(select count(*) from collection c where u.id = c.userId) as books_in_collection,
(select count(*) from books b where u.id = b.AddedByUserId) as books_added,
(select count(*) from ebooks e where u.id = e.AddedByUserId) as ebooks_added
from users u;
I don't know why you are filtering out the third user, however.

Mysql query LEFT JOIN unexpected result

mysql SELECT query with left join is not producing the result I am expecting.
I hope someone can show me or point me to the right direction,
I am trying to build a query where I get all the users name from the "users" table and
fetch the sum of all the time they spent for a particular date from the master table. I've used the left join but I am not getting the result as expected.
SUM(m.time_spent) as sum_total_time
FROM master as m
LEFT OUTER JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >= '2016-05-09'
AND m.date_created <= '2016-05-13'
GROUP BY name
ORDER BY name
master table
master_id user_id time_spent date_created
1 1 40 2016-05-01
2 2 36 2016-05-02
3 3 56 2016-05-03
4 2 33 2016-05-03
5 1 32 2016-05-05
nth nth nth number nth date
users table
user_id first_name last_name
1 James Green
2 Robert Cox
3 Andy Roger
etc etc etc
I want the output result should look like this:
user_id Name sum_total_time
1 James Green 62
2 Robert Cox 69
3 Andy Roger 56
4 Brian Harper 0
5 Angel Lee 0
6 Andrew Martin 55
.....
.....
Nth Name Nth value
You have to select data directly from the master table, group by user and calculate the sum. Then you can join this result with the user table to get all the information about the user.
Could be date conversione issue ..
SUM(m.time_spent) as sum_total_time
FROM master as m
LEFT OUTER JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >=STR_TO_DATE( '2016-05-09', '%Y-%m-%d)
and/or you have also incomplete sql
SUM(m.time_spent) as sum_total_time
FROM master as m
LEFT OUTER JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >= '2016-05-09'
AND m.date_created // this condition don't match with nothing..
// could be you forgot a part
Update 1
If you want user totale then
select u.id, u.name, SUM(m.time_spent) as sum_total_time
FROM master as m
Inner JOIN users as u ON u.user_id = m.user_id
WHERE m.date_created >=STR_TO_DATE( '2016-05-09', '%Y-%m-%d)
AND m.date_created <= =STR_TO_DATE('2016-05-13'', '%Y-%m-%d)
Group by u.id