How to write a MySQL Join query - mysql

I've two tables.
users:
uid | city | username | flag |
10 | New York | john | 1 |
14 | Tokyo | kawasaki | 1 |
15 | Tokyo | coder | 1 |
groupmember:
id | uid | groupid |
1 | 10 | 16 |
2 | 14 | 16 |
3 | 15 | 21 |
The 'uid' in both the tables are the same.
I want to select all users who are in city "tokyo" who are also in the group with groupid "16" .
So the query resutl should be (in this case)
14 | Tokyo | kawasaki | 1 |

SELECT u.uid, u.city, u.username, u.flag
FROM users u
JOIN groupmember g ON u.uid = g.uid
WHERE u.city = 'Tokyo'
AND g.groupid = 16;

select u.* from users u join groupmember gm on u.uid = gm.uid
where u.city='Tokyo' and gm.groupid=16

SELECT * FROM
users INNER JOIN groupmember
ON users.uid = groupmember.uid
AND groupmember.groupid = 16
AND users.city = 'Tokyo'

SELECT u.uid, u.city, u.username, u.flag
FROM users u, groupmember g
WHERE u.uid = g.uid
AND u.city = 'Tokyo'
AND g.groupid = 16;

Related

Select COUNT with sub-query or separate query

I'm using DataTables to display the data. It requires the total count of rows, So which approach is better for this case?
1- Sub-query:
SELECT u.id, u.name, (SELECT COUNT(id) FROM users WHERE active = 1) AS total_count FROM employees e JOIN users u ON u.id = e.user_id WHERE u.active = 1
This would return:
___________________________
| id | name | total_count |
|____|______|_____________|
| 1 | John | 7 |
| 2 | Mark | 7 |
| .. | .. | 7 |
|____|______|_____________|
2- Separate query:
SELECT u.id, u.name FROM employees e JOIN users u ON u.id = e.user_id WHERE u.active = 1
SELECT COUNT(id) FROM users WHERE active = 1
This 1st query would return:
____________
| id | name |
|____|______|
| 1 | John |
| 2 | Mark |
| .. | .. |
|____|______|
The 2nd one would return:
_____________
| COUNT(id) |
|___________|
| 7 |
|___________|
If the dataset is small then the 1st query wouldn't harm.The only drawback is since inline view is part of select statement it will get executed for each record being returned by the join between employees and users.
Here is a better option from performance standpoint,the inline view represents a table and the total_count can now be part of select statement :
SELECT u.id, u.name,total_count.tot_count
FROM (SELECT COUNT(id) tot_count FROM users WHERE active = 1) AS total_count,
employees e JOIN users u
ON u.id = e.user_id WHERE u.active = 1;

How to limit and search number of joined rows from table in multiple joins in mysql

i have a following tables in MySQL database:
+------------------------+
| Users |
+----+--------+----------+
| id | name | role |
+----+--------+----------+
| 1 | Martin | admin |
+----+--------+----------+
| 2 | George | admin |
+----+--------+----------+
| 3 | John | employee |
+----+--------+----------+
+-------------------------+
| Forms |
+----+--------------------+
| id | type |
+----+--------------------+
| 10 | marketing_form |
+----+--------------------+
| 11 | client_survey_form |
+----+--------------------+
| 12 | client_survey_form |
+----+--------------------+
+---------------------------------------------+
| UsersAssignToForms |
+----+---------+---------+--------------------+
| id | user_id | form_id | additional_comment |
+----+---------+---------+--------------------+
| 20 | 1 | 10 | Lorem ipsum... |
+----+---------+---------+--------------------+
| 21 | 2 | 10 | Lorem ipsum.... |
+----+---------+---------+--------------------+
| 22 | 3 | 10 | null |
+----+---------+---------+--------------------+
| 23 | 3 | 11 | null |
+----+---------+---------+--------------------+
I would like to have result:
+---------+---------+------------+--------------------+--------------------+
| user_id | form_id | first_name | form_type | additional_comment |
+---------+---------+------------+--------------------+--------------------+
| 1 | 10 | Martin | marketing_form | Lorem ipsum... |
+---------+---------+------------+--------------------+--------------------+
| 3 | 11 | John | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
| null | 12 | null | client_survey_form | null |
+---------+---------+------------+--------------------+--------------------+
First of all i would like to limit number of users returned from join query (one user per one form). If user with admin role is assigned to form i would like to display this user (prioritize admin role over employee role) and limit number of returned users to 1, if admin is not assign, but employee is assigned query should return this user, if no-one is assign query should return nulls (left or right join probably).
I saw this question on stackoverflow - MySQL JOIN with LIMIT 1 on joined table, but unfortunately first answer has n+1 issue and rest of answers was made with simple one join. For my purposes i need to join more tables but wouldn't like to design this tables above to clarify what i would like to achieve, but it's very important.
So my query will looks like probably:
SELECT u.id, f.id, u.name, f.type, uf.additional_comment, [more selects from other tables...] FROM Forms as f
LEFT JOIN Users as u ON ......
INNER JOIN UsersAssignToForms as uf ON .....
[here i would like to put more and more inner joins.....]
In MySql >= 8.0 you can number the rows using some criteria (for each Form starting from one and order by u.role ASC and u.id ASC), then you can filter rows with number one:
WITH sq AS (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment,
ROW_NUMBER() OVER (PARTITION BY f.id ORDER BY u.role ASC, u.id ASC) AS num
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id)
SELECT *
FROM sq
WHERE num = 1;
Before MySql 8.0 you can try something like this (the idea is the same but with different implementation):
SELECT sq2.user_id, sq2.form_id, sq2.name, sq2.type, sq2.additional_comment
FROM (
SELECT
sq1.*,
#row_number:=CASE WHEN #form_id = sq1.form_id THEN #row_number + 1 ELSE 1 END AS num,
#form_id:= sq1.form_id
FROM (SELECT u.id AS user_id, f.id AS form_id, u.name, f.type, uf.additional_comment
FROM Forms AS f
LEFT JOIN UsersAssignToForms AS uf ON f.id = uf.form_id
LEFT JOIN Users AS u ON u.id = uf.user_id
ORDER BY f.id ASC, u.role ASC, u.id ASC) AS sq1
ORDER BY sq1.form_id) AS sq2
WHERE sq2.num = 1;

sql query to get friends based on email

I have sample data that looks like this
| users |
| user_id | email |
|---------|--------------------------|
| 1 | test#example.com |
| 2 | Kanchhi#example.com |
| 3 | modi#example.com |
| 4 | andy#example.com |
| 5 | maya#example.com |
| 6 | jetli#example.com |
| 7 | john#example.com |
| user_relations |
| user_relation_id | requestor_user_id | receiver_user_id | friend_status |
|------------------|-------------------|------------------|---------------|
| 1 | 2 | 4 | 1 |
| 2 | 2 | 6 | 1 |
| 3 | 2 | 7 | 1 |
| 4 | 5 | 2 | NULL |
| 5 | 5 | 7 | NULL |
| 6 | 7 | 2 | NULL |
| 7 | 7 | 4 | 1 |
| 8 | 7 | 5 | 1 |
| 9 | 7 | 6 | 1 |
| 10 | 4 | 2 | 1 |
| 11 | 4 | 3 | 1 |
| 12 | 4 | 5 | 1 |
| 13 | 4 | 6 | 1 |
| 14 | 4 | 7 | 1 |
If input is these two emails:
Kanchhi#example.com, john#example.com
Then my expected expected output is this (order does not matter):
andy#example.com
jetli#example.com
In the above example, the friends of user id 2 is user ids (4, 6, 7) and friend of user id 7 is user ids (2, 4, 5, 6). So the mutual friend of user id 2 and 7 is 4 and 6. I need email address of mutual user id.
Another example input is:
andy#example.com, john#example.com
Then expected output is this:
jetli#example.com
Kanchhi#example.com
maya#example.com`
In the above example, friend of user id 4 is (2, 6, 5, 3, 2) and friend of user id 7 is (6, 5, 2, 4). So mutual friend user id will be 2, 6, 5. I need email address of these user id in output.
Query 1- I tried but got wrong result:
SELECT u.email
FROM user_relations r
LEFT JOIN users u ON r.requestor_user_id = u.user_id
LEFT JOIN users z ON r.receiver_user_id = z.user_id
where u.email in ('Kanchhi#example.com','john#example.com') or
z.email in ('Kanchhi#example.com','john#example.com')
and r.friend_status = 1
group by u.email
having count(u.email ) > 1
Results- but not correc:
| email |
|---------------------|
| andy#example.com |
| john#example.com |
| Kanchhi#example.com |
How to get this?
The crux of the problem is building a list of users that are friends of Kanchi and John or have them as friends. Then count those users that appear in the list twice:
-- SELECT email FROM users WHERE userid IN (
SELECT friendid
FROM (
SELECT requestor_user_id AS userid, receiver_user_id AS friendid
FROM user_relations
WHERE friend_status = 1 AND requestor_user_id IN (
SELECT user_id
FROM users
WHERE email IN ('Kanchhi#example.com','john#example.com')
)
UNION ALL
SELECT receiver_user_id, requestor_user_id
FROM user_relations
WHERE friend_status = 1 AND receiver_user_id IN (
SELECT user_id
FROM users
WHERE email IN ('Kanchhi#example.com','john#example.com')
)
) AS X
GROUP BY friendid
HAVING COUNT(DISTINCT userid) = 2
Matching the results with users is trivial. For your sample input the result is:
4 Andy andy#example.com ashutosh 2019-01-11 13:34:05
6 jetli jetli#example.com ashutosh 2019-01-11 13:34:05
You can start from the user, then join the relations.
Then group on the receiver & requestor that are different from the user.
SELECT
case when u.user_id = r.receiver_user_id then requestor.email else receiver.email end as friend_email
FROM users u
JOIN user_relations r
ON (r.requestor_user_id = u.user_id OR r.receiver_user_id = u.user_id)
AND r.friend_status = 1
LEFT JOIN users requestor ON requestor.user_id = r.requestor_user_id
LEFT JOIN users receiver ON receiver.user_id = r.receiver_user_id
WHERE u.email in ('Kanchhi#example.com','john#example.com')
GROUP BY friend_email
HAVING COUNT(DISTINCT u.user_id) > 1
Result:
friend_email
----------------
andy#example.com
jetli#example.com
You could use this and use index in join coulmn for performance
SELECT DISTINCT u.email
FROM
(
SELECT r.receiver_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.requestor_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'Kanchhi#example.com'
UNION ALL
SELECT r.requestor_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.receiver_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'Kanchhi#example.com'
) k
INNER JOIN
(
SELECT r.receiver_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.requestor_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'john#example.com'
UNION ALL
SELECT r.requestor_user_id AS id, u.email
FROM user_relations r
INNER JOIN users u
ON r.receiver_user_id = u.user_id
WHERE r.friend_status = 1 AND u.email = 'john#example.com'
) j
ON k.id = j.id
INNER JOIN users u
ON k.id = u.user_id
AND u.email NOT IN ('Kanchhi#example.com','john#example.com');
Try this
SELECT usu.email from users usu where usu.user_id in (
select r.receiver_user_id
from sov.user_relations r
where r.requestor_user_id in (
SELECT u.user_id from users u where u.email in ('Kanchhi#example.com','john#example.com')
)
and r.friend_status = 1
group by r.receiver_user_id
having count(r.receiver_user_id) > 1
);
you can create extra function like below code
BEGIN
DECLARE limitCount INT DEFAULT 0;
DECLARE counter INT DEFAULT 0;
DECLARE res INT DEFAULT 0;
DECLARE temp TEXT;
SET limitCount = 1 + LENGTH(inputList) - LENGTH(REPLACE(inputList, ',',''));
simple_loop:LOOP
SET counter = counter + 1;
SET temp = SUBSTRING_INDEX(SUBSTRING_INDEX(inputList,',',counter),',',-1);
SET res = FIND_IN_SET(temp,targetList);
IF res > 0 THEN LEAVE simple_loop; END IF;
IF counter = limitCount THEN LEAVE simple_loop; END IF;
END LOOP simple_loop;
RETURN res;
END
AND USE THIS FUNCTION LIKE THIS
find_in_set_extra('Kanchhi#example.com, john#example.com','john#example.com') OR WHAT EVER YOUR INPUTS AND OUTPUTS.

join tables to display posts of people who I follow

I want to display the posts of people who I follow
The 3 tables I have are:
Users:
+---------+------+
| id_user | name | last_logout
+---------+------+
| 1 | A | 22-02-2018 00:00:10
| 2 | B |
| 3 | C |
| 4 | D |
| 5 | E |
+---------+------+
Community:
+-------------+-------------+
| id_follower | id_followed |
+-------------+-------------+
| 1 | 2 |
| 1 | 3 |
| 1 | 5 |
+-------------+-------------+
Posts:
+---------+--------------+---------------+
| id_post | id_user_post | post | date
+---------+--------------+---------------+
| 1 | 2 | hi |
| 2 | 3 | hello |
| 3 | 5 | hey you |
| 4 | 4 | come on |
| 5 | 5 | where are you | 22-02-2018 00:01:00
+---------+--------------+---------------+
I'm using the following code but it doesn't return anything
SELECT u.name AS n
,p.post AS t
FROM community AS c
LEFT JOIN users AS u ON u.id_user = c.id_followed
LEFT JOIN posts AS p ON c.id_followed = p.id_user_post
WHERE u.id_follower = 1
Users.id_follower does not exist, so that is why you get nothing, in fact you are likely getting an error (Invalid column name 'id_follower'.).
Use Community.id_follower instead. I would also recommend using more descriptive column names (like 'username' and 'comment')
For the pure reason of answering your specific question, I have used 'n' and 't' in the query.
SELECT u.name as n, p.post as t
FROM Community c
LEFT JOIN Users u ON c.id_followed = u.id_user
LEFT JOIN Posts p ON c.id_followed = p.id_user_post
WHERE c.id_follower = 1
Test:
;WITH USERS AS(
SELECT *
FROM (VALUES (1,'A'),
(2,'B'),
(3,'C'),
(4,'D'),
(5,'E')) U(id_user, name))
, Community AS(
SELECT *
FROM (VALUES (1,2),
(1,3),
(1,5)) C(id_follower, id_followed))
, posts AS(
SELECT *
FROM (VALUES (1,2,'hi'),
(2,3,'hello'),
(3,5,'hey you'),
(4,4,'come on'),
(5,5,'where are you')) P(id_post, id_user_post, post))
SELECT u.name as n, p.post as t
FROM Community c
LEFT JOIN Users u ON c.id_followed = u.id_user
LEFT JOIN Posts p ON c.id_followed = p.id_user_post
WHERE c.id_follower = 1

MySQL: Search group from constituent members using AND condition

Following three SQL tables describe who belongs to which group. For example, Ann belongs to group A/B, Ben belongs to group B/C, and Chris belongs to group A/C.
Now, when I want to search "a group that both Ann AND Ben belong", how should we write the SELECT query? or should we design these tables in different way?
(1) users
+-----------+------------------+
| member_id | member_name |
+-----------+------------------+
| 1 | Ann |
| 2 | Ben |
| 3 | Chris |
+-----------+------------------+
(2) groups
+-----------+------------------+
| group_id | group_name |
+-----------+------------------+
| 1 | Group A |
| 2 | Group B |
| 3 | Group C |
+-----------+------------------+
(3) users_groups
+-----------+------------------+
| group_id | user_id |
+-----------+------------------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 3 | 2 |
| 1 | 3 |
| 3 | 3 |
+-----------+------------------+
SELECT group_id, group_name
FROM groups
JOIN users_groups ON groups.group_id = users_groups.group_id
JOIN users ON users_groups.user_id = users.member_id
WHERE member_id = 1
INTERSECT
SELECT group_id, group_name
FROM groups
JOIN users_groups ON groups.group_id = users_groups.group_id
JOIN users ON users_groups.user_id = users.member_id
WHERE member_id = 2
This will give the group id and name of all groups where both Ann and Ben are in. Feel free to replace the member_id = # clause with member_name = 'name' if you want to search by their name instead of their id.
EDIT: I forgot that MySQL doesn't have INTERSECT. So the above will work for most DBMSs, but the below should return the desired results for MySQL.
SELECT groups.group_id, groups.group_name
FROM groups
JOIN users_groups ON groups.group_id = users_groups.group_id
JOIN users ON users_groups.user_id = users.member_id
WHERE member_id = 1
AND groups.group_id IN (SELECT groups.group_id
FROM groups
JOIN users_groups ON groups.group_id = users_groups.group_id
JOIN users ON users_groups.user_id = users.member_id
WHERE member_id = 2)