Select rows which have no match in other table - mysql

This query seemed pretty simple to start with.
#table - user
user_id - full_name - image
1 Jon-1 Jon-placeholder.png
2 Aax-2 Max-placeholder.png
3 Lie-3 Lie-placeholder.png
4 Man-4 Man-placeholder.png
5 Nik-5 Nik-placeholder.png
6 Led-6 Led-placeholder.png
7 Neo-7 Neo-placeholder.png
#table - user_contacts
id - user_id - contacts_id - created_date
1 1 2 2020-02-26 08:43:26
2 1 3 2020-02-26 08:43:26
3 1 5 2020-02-26 08:43:26
4 3 4 2020-02-26 08:43:26
5 3 5 2020-02-26 08:43:26
6 7 2 2020-02-26 08:43:26
What i am trying to a accomplish is retrieve all contacts_id from user_contacts where the user_id is not equal to user_id = 1 which user Jon-1 so this must looks like
#retrieved data
user_id - full_name - image
4 Man-4 Man-placeholder.png
6 Led-6 Led-placeholder.png
7 Neo-7 Neo-placeholder.png
user_ids 6,7,4 must be retrieved because user_id = 1 does not have those ids in the user_contact table under column contacts_id
First Query
SELECT u.user_id, u.full_name, u.image
FROM user u
NATURAL LEFT JOIN user_contacts cnt
WHERE cnt.contacts_id IS NULL
Second Query
SELECT user.user_id, user.full_name, user.image
FROM USER
WHERE user.user_id NOT IN (
SELECT user_contacts.contacts_id
FROM user_contacts
WHERE user_contacts.contacts_id IS NOT NULL)
Third Query
SELECT u.user_id, u.full_name, u.image
FROM user u
NATURAL LEFT JOIN user_contacts cnt
WHERE cnt.contacts_id IS NULL

dbfiddle
SELECT
user_id, full_name, image
FROM
user
WHERE NOT user_id in (SELECT contacts_id from user_contacts WHERE user_contacts.user_id=1)
AND user_id <> 1
WHERE NOT user_id in sub-query: The sub-query will get the contact for user=1.
AND user_id <> 1: this makes sure user 1 is not selected.

With NOT EXISTS:
select u.* from user u
where user_id <> 1
and not exists (
select 1 from user_contacts
where user_id = 1 and contacts_id = u.user_id
)
See the demo.
Results:
| user_id | full_name | image |
| ------- | --------- | ------------------- |
| 4 | Man-4 | Man-placeholder.png |
| 6 | Led-6 | Led-placeholder.png |
| 7 | Neo-7 | Neo-placeholder.png |

SELECT a.*
FROM user a
LEFT
JOIN
( SELECT uc.*
FROM user u
JOIN user_contacts uc
ON uc.user_id = u.user_id
WHERE u.full_name = 'Jon-1'
) b
ON b.contacts_id = a.user_id
WHERE a.full_name <> 'Jon-1'
AND b.id IS NULL;

Related

Get list of groups user hasn't Joined, List of groups user didn't create

The user (Ben) has joined group 2 and group 3. How can I write this in a select query... I want to select from groups I haven't joined and groups I didn't create.
users_tbl table
user_id username
| 1 | ben
| 2 | betty
| 3 | tim
| 4 | jimmy
| 5 | sammy
user_groups table
user_id group_id
| 1 | 2
| 1 | 3
group_tbl table
group_id user_id
| 1 | 5
| 2 | 4
| 3 | 5
I am able to get the list of groups I didn't create using this query...
SELECT * FROM group_tbl LEFT JOIN users_tbl ON users_tbl.user_id = group_tbl.user_id WHERE group_tbl.user_id != ? ORDER BY RAND() LIMIT 10
How can I get the list of groups users hasn't joined?
You can do it if you do a LEFT join of group_tbl to users_tbl and return the unmatched rows of group_tbl:
SELECT g.*
FROM group_tbl g LEFT JOIN user_groups u
ON u.group_id = g.group_id AND u.user_id = 1
WHERE u.user_id IS NULL
Or with NOT EXISTS:
SELECT g.*
FROM group_tbl g
WHERE NOT EXISTS (
SELECT 1
FROM user_groups u
WHERE u.group_id = g.group_id AND u.user_id = 1
)
See the demo.
Results:
group_id
user_id
1
5

How to select only one record of the left table based on its max date in MySQL?

Below is my database structure
User
id name
1 John
2 Doe
3 Smitt
Post
id user_id text
1 1 Hello
2 1 World
3 2 How are you?
4 3 Whatsup!
5 3 High five!
Comment
id post_id text created_at
1 1 Comment on Hello 2019-12-01
2 1 Another comment on Hello 2019-12-02
3 2 Comment on World 2019-12-03
4 1 Latest comment on Hello 2019-12-04
5 1 Some comment 2019-12-05
6 5 Five highs! 2019-12-06
7 4 Same old, same old! 2019-12-07
How to get a list of user with only one Comment record based on its MAX(created_at) in MySQL as following?
Result
id name comment_text created_at
1 John Some comment 2019-12-05
2 Doe NULL NULL
3 Smitt Same old, same old! 2019-12-07
Condition: due to another use case, the flow must go from User table to Comment table, not vice versa!
You can left join, using a correlated subquery that retrieves the id of the latest post of the current user as a join condition:
select
u.id,
u.name,
c.text comment_text,
c.created_at
from user u
left join comment c
on c.id = (
select c1.id
from post p1
inner join comment c1 on c1.post_id = p1.id
where p1.user_id = u.id
order by c1.created_at desc
limit 1
)
Demo on DB Fiddle:
id | name | comment_text | created_at
---: | :---- | :------------------ | :---------
1 | John | Some comment | 2019-12-05
2 | Doe | null | null
3 | Smitt | Same old, same old! | 2019-12-07
Or, if you are running MySQL 8.0, you can use row_number():
select id, name, comment_text, created_at
from (
select
u.id,
u.name,
c.text comment_text,
c.created_at,
row_number() over(partition by u.id order by c.created_at desc) rn
from user u
left join post p on p.user_id = u.id
left join comment c on c.post_id = p.id
) t
where rn = 1
Demo on DB Fiddle

MySQL order by before group by did not work

I have two tables
users
user_id | name
1 | John
2 | Jess
user_data (in_out - 1 indicate in, 0 indicate out)
id | user_id | in_time | out_time | in_out
1 | 1 | 2019-05-14 09:15:32 | 2019-05-14 10:45:32 | 0
2 | 1 | 2019-05-15 10:15:32 | 0000-00-00 00:00:00 | 1
3 | 2 | 2019-05-16 11:15:32 | 2019-05-16 12:15:32 | 0
I want to get latest entries of each user, but group by and order by did not work well.
First I tried following way, but this is not give latest records (I want to get record id 2 and 3 from user_data table, but it returns record id 1 and 3)
SELECT *, user.user_id as user_key
FROM user
LEFT JOIN user_data ON user_data.user_id = user.user_id
GROUP BY user_data.user_id ORDER BY user_data.id DESC
Secondly I tried following way, I wrote this query following an answer of stackoverflow, but it did not work.
SELECT *, user.user_id as user_key
FROM user
LEFT JOIN
(
SELECT MAX(user_data.id) as max_record_id, user_data.user_id
FROM user_data
GROUP BY user_data.user_id
) u2 ON u2.user_id = user.user_id
GROUP BY user_data.user_id ORDER BY user_data.id DESC
Someone please help me to solve this issue. Thank You
You can try using a correlated subquery
SELECT *, user.user_id as user_key
FROM user
LEFT JOIN user_data u2 ON u2.user_id = user.user_id
where u2.id in (select MAX(u3.id) from user_data u3 where u2.user_id=u3.user_id)
ORDER BY u2.id

How to optimize SQL query more?

First at all i am nood into SQL thing, Now i am working on a class project where
I have some tables like
Table user
user_id | username | name
1 | nihan | Nihan Dip
2 | dip | Meaw ghew
more | more | more
Table Friend
you | friend_id
1 | 2
1 | 27
2 | 9
more | more
Table Follow
user_id | follows
1 | 99
7 | 34
Table post
post_id | user_id | type | content | post_time
1 | 1 | text | loren toren | timestamp
2 | 2 | text | ipsum | timestamp
Now i want to get post by users friend and who he follows and offcourse his so i made this SQL
SELECT
username, name,content, post_time
FROM
post
INNER JOIN
user ON user.user_id = post.user_id
WHERE
post.user_id IN (SELECT
friend_id
FROM
friend
WHERE
you = 1
UNION ALL
SELECT
follows
FROM
follow
WHERE
user_id = 1)
OR post.user_id = 1
ORDER BY post_time DESC
LIMIT 10
this query works just fine. I just wanted to know is there anymore optimization could be done? Then how? Please teach me :)
Instead of using IN try it with JOIN add add few more indexes.
SELECT DISTINCT u.name, u.username,
p.content, p.post_time
FROM post p
INNER JOIN user u
ON u.user_id = p.user_id
INNER JOIN
(
SELECT friend_id id
FROM friend
WHERE you = 1
UNION ALL
SELECT follows id
FROM follow
WHERE user_id = 1
) s ON p.user_id = s.ID
ORDER BY post_time DESC
LIMIT 10

Mysql SUM based another calculated filed condition

There are three tables Users, Lists, Details. Here it is my table structure and data sample
userid username | list_id list_val user_id | detail_id list_id multipler
1 user1 | 1 500 1 | 1 1 3
2 user2 | 2 300 1 | 2 1 2
3 user3 | 3 600 1 | 3 2 1
4 100 2 4 2 1
5 3 4
SELECT
users.username,
SUM(lists.lists_var),
FROM
users
INNER JOIN lists ON users.userid = lists.user_id
GROUP BY
users.username
HAVING
(SELECT (exp(sum(log(COALESCE(details.multipler, 1))))) FROM details WHERE
details.list_id = lists.list_id) > 3
This query gives me result
user1 - 1400
But I want something IF multipler_total > 3 THEN SUM(list_val) so result must be:
user1 - 1100
try this:
SELECT
users.username,
SUM(lists.lists_var),
FROM
users
INNER JOIN lists ON users.userid = lists.user_id
INNER JOIN (SELECT list_id, SUM(multiplier) mult_total FROM details GROUP BY list_id) d
ON d.list_id = lists.list_id and d.mult_total > 3
GROUP BY
users.username