many to many mysql with tricky condition - mysql

Please, check my tables below
users
id name
1 John
2 Peter
3 Predator
phones
id name
1 apple
2 nexus
3 nokva
phones_users
phone_id user_id
1 1
2 1
2 2
3 3
sqlfiddle
Ok, I want to get a result like this:
phone_id phone_name user_id user_name
1 apple 1 John
2 nexus 1 John
2 nexus 2 Peter
I tried one request, but it doesn't work expectedly
select `phones`.`name` as `phone_name`, `phones`.`created_at`, `phones`.`id`, `users`.`name` as `user_name`, `users`.`id` as `user_id` from `phones` left join `phones_users` on (`phones`.`id` = `phones_users`.`phone_id` and `phones_users`.`user_id` = 1) left join `users` on `phones_users`.`phone_id` = `phones`.`id` where `users`.`name` is not null;
I want to get all the phones that have the user with ID = 1, and all users who have phones that belonged to him..
For example, John has apple and nexus and Peter has nexus, I want to get a table with apple, nexus, John, Peter.
THANKS

I attached a copy of phones_users and used IF to distinguish, whether the phone belongs to the same person or not. Based upon that I output the user name and id.
SELECT
`phones_users`.`phone_id`,
`phones`.`name` AS phone_name,
IF(`users`.`id` = `users2`.`id`, `users`.`id`, `users2`.`id`)
AS `user_id`,
IF(`users`.`id` = `users2`.`id`, `users`.`name`, `users2`.`name`)
AS `user_name`
FROM
phones_users
INNER JOIN
phones
ON
phones_users.phone_id=phones.id
INNER JOIN
users
ON
phones_users.user_id=users.id
INNER JOIN -- here I attach the copy of the table
phones_users AS phones_users2
ON
phones_users.phone_id=phones_users2.phone_id
INNER JOIN
users AS users2
ON
phones_users2.user_id=users2.id
WHERE
phones_users.user_id=1;
Output:
+----------+--------------+---------+-----------+
| phone_id | phone_name | user_id | user_name |
+----------+--------------+---------+-----------+
| 1 | apple iphone | 1 | John |
| 2 | nexus | 1 | John |
| 2 | nexus | 2 | Peter |
+----------+--------------+---------+-----------+

-- We know the user details for this part of the query, so just hard-code them instead of query for them
SELECT phone_id, (SELECT name FROM phones WHERE id = pu.phone_id) AS phone_name, 1 AS user_id, "John" AS user_name
FROM phones_users pu
WHERE phone_id IN (SELECT phone_id
FROM phones_users
WHERE user_id = 1)
UNION ALL
SELECT p.id AS phone_id, p.name AS phone_name, u.id AS user_id, u.name AS user_name
FROM phones p, user u
WHERE u.id IN (SELECT user_id
FROM phones_users
WHERE user_id != 1
AND phone_id IN (SELECT phone_id FROM phones_users WHERE user_id = 1))
AND u.id = (SELECT user_id
FROM phones_users
WHERE phone_id = p.id)
Transcribed to English:
Select the phone_id from the table phones_users where this id is in the list of ids of phones used by John and the name of the phone with this id and the values 1 (the ID of John) and his name, "John".
Then, append to that (here it starts to get even more complicated):
the phone id and name and user id and name from the rows resulting from joining the tables phones and users, but only on the rows satisfying the following constraints:
The user id of the row is in the list of user ids of users whose phone's id is the same as the id of the phone John (the user with id 1) uses, i.e. this user is not John but uses the same phone as John,
and the user id is in the list of user ids that are in the table phones_users in the same row as the phone id of the joined row, i.e. this user does indeed use this phone (joining the tables phones and users will give all possible combinations, so we have to remove the rows in which people are with phones they don't use).
I hope you can understand the transcription and the query, if not feel free to leave a comment.
DISCLAIMER: I didn't test the query, it may not work.

Related

SQL select query to get total unique entries from two foreign key tables based on a column value

I've got three tables as shown below - ideally it wouldn't be laid out like this but currently no power change it.
Team User Member
ID | Name ID | TeamId | Email ID | TeamId | Email
----------
1 | Team A 1 | 1 | a#email.com 1 | 1 | a#email.com
2 | Team B 2 | 1 | b#email.com 2 | 1 | b#email.com
3 | Team C 3 | 1 | c#email.com
I need to be able to get the combined count of users and members in each team, uniquely based on their email address.
So for example, Team A would have a unique count of combined members and users of 3.
An entry may exist in either the user table OR the member table, or in both for each email.
The outcome of the query would be TeamName and TotalUsers.
Any help with this type of query would be greatly appreciated.
Use UNION to collect all the distinct combinations of team ids and emails from User and Member and do a LEFT join of Team to that resultset and aggregate:
SELECT t.id, t.name,
COUNT(email) count
FROM Team t
LEFT JOIN (
SELECT teamid, email FROM User
UNION
SELECT teamid, email FROM Member
) e ON e.teamid = t.id
GROUP BY t.id;
you can UNION members and User table, so that duplicates would be removed
And then join it to the temas table
SELECT
t1.Name, COUNT(DISTINCT Email)
FROM
Team t1
JOIN
( SELECT TeamId , Email FROM User
UNION SELECT TeamId , Email FROM Member) t2 ON t2.TeamId = t1.ID
GROUP BY t1.Name

Update Records in Table based on query result and variable - result only returns record for first int in variable

I want to update records in table Users that are not present in table UserActions (see sqlfiddle demo or sql and data at gist.github)
My tables
Table Users
ID | UserName | isActive
1 | Ben Busy | 1
2 | Lui Lazy | 1 <-- never logged in
3 | Emmy Eager | 1
4 | Lana Later | 1 <-- never logged in
Table UserActions
ID | User_ID | Type | ActionDate
1 | 1 | Login | 2021-01-01 <-- Joe
2 | 3 | Login | 2021-01-02 <-- Eda
3 | 1 | Login | 2021-01-02 <-- Joe
4 | 1 | Login | 2021-01-03 <-- Joe
I want to set isActive = 0 for all Users that never logged in.
This query returns the userIDs that never logged in:
SELECT ID FROM Users u LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL
I was not able to use this question or this question so i thought that using a varible SET #userIDs := (....) should work as well
SET #userIDs := (
SELECT GROUP_CONCAT(ID SEPARATOR ', ') FROM Users u LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL);
The varible #userIDs contains all the relevant user-ids that never logged in (2,4).
But this statement
SELECT * FROM Users where ID in (#userIDs);
only returns the first result.
Questions
Why does where ID in #myVar not work?
Is there a way besides using a temporary table?
Code and Data
sql and data at gist
sqlfiddle demo
Perhaps you can just use NOT EXISTS:
UPDATE Users u
SET u.IsActive=0
WHERE NOT EXISTS
(SELECT user_id FROM UserActions ua
WHERE (ua.Type = "Login" OR ua.Type = NULL) AND u.ID=ua.user_id);
Demo fiddle
Update 3rd party edit
If you want to use a variable you can do it with find_in_set
SET #userIDs := (
SELECT GROUP_CONCAT(u.ID SEPARATOR ',') FROM Users u
LEFT JOIN UserActions ua
ON u.ID = ua.User_ID
AND (ua.Type = "Login" OR ua.Type = NULL)
WHERE ua.ActionDate IS NULL);
And then
SELECT * FROM Users WHERE FIND_IN_SET(Users.ID, #userIDs);
See this dbfiddle

MySQL output all records from table but add flag if value present in another

I am trying to populate an autocomplete select field which needs to show all users but add a flag next to those that exist in the club table for the current club.
Users
user_id | first_name | last_name
--------------------------------
1 | Bob | Smith
2 | Lisa | Someone
3 | Bill | Green
4 | Jane | Hill
Club
club_id | user_id
-----------------
1 | 2
2 | 1
2 | 4
Output I need when I am looking for all users in the context of club "2"
1 | Bob | Smith | TRUE
2 | Lisa | Someone |
3 | Bill | Green |
4 | Jane | Hill | TRUE
I don't know why this is bending my mind so much...
Tim
You can do this with a left outer join:
select u.user_id, u.first_name, u.last_name,
(c.club_id = 2) as flag
from users u left join
club c
on u.user_id = c.user_id;
Note that the flag gets a value of 0 (false) or 1 (true). If you want some other values for the flag, then you will need to use a case statement.
EDIT:
If you simply want the flag for each user, then the easiest way is:
select u.*,
exists (select 1 from club c where u.user_id = c.user_id and c.club_id = 2) as flag
from users u;
Once again, this produces a 0/1 flag. If you want a different value, then it needs to go into a case.
Alternatively, you could use:
select u.user_id, u.first_name, u.last_name,
(c.user_id is not null) as flag
from users u left join
club c
on u.user_id = c.user_id and c.club_id = 2
How about something like
SELECT *,
CASE
WHEN EXISTS(SELECT 1 FROM Club c WHERE c.user_id = u.user_id)
THEN 'TRUE'
ELSE NULL
END FlagValue
FROM users u
SQL Fiddle DEMO
select u.user_id, u.first_name, u.last_name
, case when c.user_id is not null then 'TRUE' else '' end
from users u
left join club c
on u.user_id = c.user_id
and c.club_id = 2
order by u.user_id
I originally misread your question, sorry. Here's what you're looking for:
select u.user_id, u.first_name, u.last_name,
case when c.club_id is not null then true end as flag
from users u
left join club c on (u.user_id=c.user_id and c.club_id=2)
Just to be clear about your problem statement, I created the tables and inserted the data -
create table users (user_id bigint not null, first_name varchar(100), last_name varchar(100));
create table club (club_id bigint not null, user_id bigint not null);
insert into users values (1, 'Bob','Smith');
insert into users values (2, 'Lisa','Someone');
insert into users values (3, 'Bill','Green');
insert into users values (4, 'Jane','Hill');
insert into club values (1, 2);
insert into club values (2, 1);
insert into club values (2, 4);
And here is the query that I ran -
select users.user_id, users.first_name, users.last_name,
case when club.club_id = 2 then 'TRUE' end as flag
from users left join club on users.user_id = club.user_id;
And here's the output -
1 Bob Smith TRUE
2 Lisa Someone NULL
3 Bill Green NULL
4 Jane Hill TRUE

Select inner join with n_m table

I have three tables as following:
USERS TABLE
id_user| name |
---------------
1 | ...
2 | ...
SERVICES TABLE
id_service | name |
-------------------
1 | ...
2 | ...
3 | ...
USER_SERVICES TABLE (n-m)
id_user | id_service
--------------------
1 | 1
1 | 2
2 | 1
And I need to do a SELECT starting from "SELECT * FROM users" and then, getting the users by services. Ex. I need to get every user with services = 1 and services = 2 (and maybe he has other more services, but 1 and 2 for sure).
I did the following:
SELECT *
FROM `users`
INNER JOIN user_services ON users.id_user = user_services.id_user
WHERE id_service=1 AND id_service=2
But this, of course dont works since there is not a single record matching service = 1 and service = 2.
What can I do?
Add an extra join for the other service you want to check:-
SELECT *
FROM `users`
INNER JOIN user_services us1 ON users.id_user = us1.id_user AND us1.id_service=1
INNER JOIN user_services us2 ON users.id_user = us2.id_user AND us2.id_service=2
select t.*,
(select count(*) from user_services where id_user = t.id_user) how_much
from users t;
Is this what you want???
It shows the data of the users and how much services are in the services table. Other possibility is this:
select t.*,
(case when (select count(*)
from user_services where id_user = 1) > 0
then 'service1'
else 'null'
end) has_service_1
from users t;
The problem with this select is that you have to repeat this case...end as much times as id_services you have, so it doesn't make sense if the number of services is increasing over time. On the contrary, if it is a somewhat fixed number, and it is not a big number, this could be a solution.

MySQL Left join with count on second Table

I have looked through some of the other posts on this site and am not seeing exactly what im looking for so here goes.
Lets say i have 2 tables
juser
-----------------------------
userID firstName lastName
-----------------------------
1 billy bob
2 jezze belle
3 bobbie sue
and:
juserrel
---------------------------------------------
id userID relUserID state
---------------------------------------------
1 1 2 approved
2 2 1 retired
3 2 1 approved
4 3 2 approved
What i am trying to do is get a result set that shows each user info about each user in the juser table and adds a column called connections to the result set which shows how many "active" connections a particular user has to another user.
the result i expect based on the tables above is
resultSet
-----------------------------------------------
userID firstName lastName connections
-----------------------------------------------
1 billy bob 2
2 jezze belle 3
3 bobbie sue 1
The query I tried is as follows
select userID, firstName, lastName , coalesce(x.cnt,0) as connections
from juser
left outer join (select count(*) cnt from juserrel where juserrel.userID =
userID or juserrel.relatedUserID = userID and juserrel.state = 'approved')
x on userID = userID
The result set i get looks like this:
resultSet
-----------------------------------------------
userID firstName lastName connections
-----------------------------------------------
1 billy bob 4
2 jezze belle 4
3 bobbie sue 4
Help Please ;)
Try:
select u.userID, u.firstName, u.lastName,
count(case when ur.state = 'approved' then 1 end)
from juser u
inner join juserrel ur on u.userID = ur.userID or u.userID = ur.relUserID
group by u.userID, u.firstName, u.lastName
SQL Fiddle Example