Select only one row from a join based on foreign condition - mysql

The main idea
user hasMany roles (data is stored in the tables: users, roles, user_role)
i want to know if the user is admin OR client
this data will then be joined to a different result
What I'm doing
SELECT
`users`.`id`,
`users`.`name`,
`roles`.`display_name`
FROM `users`
JOIN role_user ON users.id = role_user.user_id
JOIN roles ON role_user.role_id = roles.id
Why it's wrong
Because this is the result I get
id Name Display name
1 admin Admin
1 admin Client
2 admin2 Admin
2 admin2 Client
3 client Client
7 test Admin
7 test Client
What I want
id Name Display name
1 admin Admin
2 admin2 Admin
3 client Client
7 test Admin
How I'm working to make this work
Using aggregates somehow
Using cases somehow
Joining with a subset of the data somehow
Thank you for your idea !
[Update] Here is an sqlFiddle describing the issue.

SELECT
`users`.`id`,
`users`.`name`,
`roles`.`display_name`
FROM `users`
LEFT JOIN role_user ON users.id = role_user.user_id
left JOIN roles ON role_user.role_id = roles.id

This solution should work fine for you:
select * from users
where users.id in (
select users.id FROM users
JOIN role_user ON (users.id = role_user.user_id)
JOIN roles ON (role_user.role_id = roles.id)
where roles.display_name like 'admin'
)
Union
select * from users
where users.id not in (
select users.id FROM users
JOIN role_user ON (users.id = role_user.user_id)
JOIN roles ON (role_user.role_id = roles.id)
where roles.display_name like 'admin'
);
Fiddle here: http://sqlfiddle.com/#!9/0fabba/42
I would recommend to create a view for the sub-query, but I couldn't do it on fiddle.
Example using a view:
Create or replace view admin_list as
select users.id FROM users
JOIN role_user ON (users.id = role_user.user_id)
JOIN roles ON (role_user.role_id = roles.id)
where roles.display_name like 'admin';
Then the query will be much compact:
select * from users
where users.id in (
select users.id FROM admin_list
)
Union
select * from users
where users.id not in (
select users.id FROM admin_list
);

My own solution was the following
create a view to get the user's role id (either admin or client)
join with the view
Specifically here is the view
CREATE VIEW vw_users_role_by_priority AS (
SELECT
role_user.user_id,
min(role_user.role_id) AS 'role_id'
FROM role_user
JOIN roles ON role_user.role_id = roles.id
GROUP BY role_user.user_id
);
And here is the final query
SELECT
`users`.`id`,
`roles`.display_name
FROM `users`
JOIN vw_users_role_by_priority ON vw_users_role_by_priority.user_id = users.id
JOIN roles ON roles.id = vw_users_role_by_priority.role_id
ORDER BY `users`.`name` ASC;

Try with this query :
SELECT users.`id`, users.`name`, MIN(roles.`display_name`)
FROM `users` users, `role_user` role_user, `roles` roles
WHERE users.id = role_user.user_id AND role_user.role_id = roles.id
GROUP BY users.`id`
In your query you join tables which give you all possible combinations of results. This query will select only the role_user/roles for each found userId.
EDIT : added "group by" to select only unique users and "min" to select its alphabetically lowest role (so "admin" get selected before "client")

Related

SQL LEFT JOIN for all records of first table and matched records of another table

I have 3 tables.
Table seller with columns like id, name etc.
Table customer like id, name etc.
Table connections which have seller_id, customer_id, status of friendship like "friends", "pending_request" etc.
Now I want to get all the sellers who are not friends of a specific customer.
So I tried like fetching records from seller table with left join of connections table, with condition status is not "friends"
I tried the following query but didn't help me. I also tried other queries but didn't help.
SELECT * FROM `seller` LEFT JOIN `connections` ON seller.user_id = connections.user_id WHERE customer_id = 10 AND request_status NOT LIKE "friends"
Here is the reference screen I want the result. Like for a particular customer, all the sellers who are not friends or request is pending.
Join the connections of type 'friends' for customer_id = 10 and in a WHERE clause check for the connections.user_id being NULL, i.e. nothing has been joined.
SELECT *
FROM seller
LEFT JOIN connections
ON seller.user_id = connections.user_id
AND connections.customer_id = 10
AND connections.request_status = 'friends'
WHERE connections.user_id IS NULL;
Or use a correlated subquery, that gets the connection with a NOT EXISTS.
SELECT *
FROM seller s
WHERE NOT EXISTS (SELECT *
FROM connections c
WHERE c.user_id = s.user_id
AND c.customer_id = 10
AND c.request_status = 'friends');
Try this:
SELECT * FROM `seller` LEFT JOIN `connections` ON seller.user_id = connections.user_id WHERE customer_id = 10 AND (request_status NOT LIKE "friends"
or request_status is null)
Or this:
SELECT * FROM `seller` LEFT JOIN `connections` ON seller.user_id = connections.user_id WHERE customer_id = 10 AND IFNULL(request_status,"other") NOT LIKE "friends"

Union two SELECT queries in one

as i can't brain this out I'm asking for your help. I'm not so familiar with UNIONS. I have two SELECT queries which i would like to merge in one.
So i want to select the name based on userId from table Teams and their gameAcc from table connections based on teamsId.
I've made two separate queries:
SELECT users.name
FROM users
JOIN teams
ON users.id = teams.usersId
WHERE teams.Id = 1
SELECT connections.gameAcc
FROM connections
JOIN users
ON connections.usersId = users.id
JOIN teams
ON connections.teamsId = teams.id
WHERE teams.id = 1
Expected: A single query that will output 1 row with users name from teams table and their gameAcc from connections table.
Example data:
And i want it to output:
|John| | JohnKiller |
Thanks for your help!
You need to join the 3 tables and not UNION:
SELECT users.name, connections.gameAcc
FROM users
INNER JOIN teams ON users.id = teams.usersId
INNER JOIN connections ON connections.teamsId = teams.id
WHERE teams.Id = 1
I used INNER joins just like your code.
Depending on the case maybe you need LEFT joins if there is no WHERE clause.
Edit, after the questions updates.
SELECT u.name, c.gameAcc
FROM connections c
JOIN users u ON c.usersId = u.id
JOIN teams t1 ON c.teamsId = t1.id
JOIN teams t2 ON u.id = t2.usersId
WHERE t2.id = 1;
EDIT:
From the comments above, you seem to want the name of the owner, and the gameAcc.
In my example below, I changed teams.usersId to "ownerId", to avoid confusion.
I'm assuming that the connections table is joined to the users table, and also to the teams table.
https://www.tutorialspoint.com/execute_sql_online.php
BEGIN TRANSACTION;
/* Create tables for example: */
CREATE TABLE USERS(Id integer PRIMARY KEY, Name text);
CREATE TABLE TEAMS(Id integer PRIMARY KEY, Name text, ownerId integer);
CREATE TABLE CONNECTIONS(Id integer PRIMARY KEY, usersId integer, teamsId integer, gameAcc text);
/* Create new records in these tables */
INSERT INTO USERS VALUES(1,'Jim');
INSERT INTO USERS VALUES(2,'Paul');
INSERT INTO USERS VALUES(3,'Tony');
INSERT INTO USERS VALUES(4,'Bill');
INSERT INTO USERS VALUES(5,'Art');
INSERT INTO TEAMS VALUES(11,'Tigers', 1); -- owner = user.id = 1
INSERT INTO TEAMS VALUES(22,'Raptors', 2); -- owner = user.id = 2
INSERT INTO CONNECTIONS VALUES(111, 1, 11, 'gameAcc1');
INSERT INTO CONNECTIONS VALUES(222, 2, 22, 'gameAcc2');
INSERT INTO CONNECTIONS VALUES(333, 3, 22, 'gameAcc2');
INSERT INTO CONNECTIONS VALUES(444, 4, 22, 'gameAcc2');
INSERT INTO CONNECTIONS VALUES(555, 5, 11, 'gameAcc2');
COMMIT;
/* Display desired records from the tables */
SELECT users.name, connections.gameAcc
FROM users
JOIN connections ON connections.usersId = users.id
JOIN teams ON connections.teamsId = teams.id
WHERE teams.id = 11 AND teams.ownerId = users.id
Split it on steps.
First you want to know the owner
SELECT t.userid
FROM teams t
WHERE t.teamid = 1
Now you want the owner name
SELECT t.userid, u.name
FROM teams t
JOIN users u
ON t.userid = u.id
WHERE t.teamid = 1
Now you want the game acc for the owner
SELECT t.userid, u.name, c.gameAcc
FROM teams t
JOIN users u
ON t.userid = u.id
JOIN connection c
ON t.userid = c.userid
WHERE t.teamid = 1

create mysql view for many-to-many relationship

MYSQL script to create a MYSQL view Table to show use id , email , and the assigned role/s name/s through many-to-many relationship
I wrote the script and joined tables to create the view table but it shows role name for the first record only the other users has null although they have roles!.
Tables
users (id, email)
roles (id, name)
role_users (id, user_id, role_id)
My script
DROP VIEW IF EXISTS users_view;
CREATE VIEW users_view AS
SELECT users.id AS user_id, users.email, roles_names.role_name
FROM users
LEFT JOIN(
SELECT role_users.user_id AS roles_user_id,
role_users.role_id AS roles_role_id
FROM role_users
) AS user_roles ON (roles_user_id = users.id)
LEFT JOIN(
SELECT roles.id AS roleid, roles.name AS role_name
FROM roles
) AS roles_names ON (roleid = user_roles.roles_user_id)
view created success but it shows the role name for the first user only , other users has role_name value of null although they have records at role_users table
SELECT u.id AS user_id, u.email, IFNULL(u_roles.role_name,'') as role_name
FROM users u
LEFT JOIN
(SELECT GROUP_CONCAT(DISTINCT r.name SEPARATOR ',') as role_name, ur.user_id
FROM roles r INNER JOIN user_roles ur
ON r.id = ur.role_id
GROUP BY ur.user_id) u_roles
ON u.id = u_roles.user_id
Since role users will be having all only roles from roles table, inner join on roles and user_roles will be fine.
By this query all records which are having an entry in user_roles table will show role name and rest of the users will be having null as role name (adding IFNULL check to make null values an empty string)

SQL select for friendships table in cometchat

Im beginner in sql soo i have big problem with this "big" select for friends table in cometchat. Can somebody write it for me.
Cometchat is configured for friends table :
friends
toid fromid
id 2 1
id 1 2
and sql select for it:
$sql = ("select DISTINCT ".TABLE_PREFIX.DB_USERTABLE.".".DB_USERTABLE_USERID." userid, ".TABLE_PREFIX.DB_USERTABLE.".".DB_USERTABLE_NAME." username,
".TABLE_PREFIX.DB_USERTABLE.".".DB_USERTABLE_LASTACTIVITY." lastactivity,
".DB_AVATARFIELD." avatar, ".TABLE_PREFIX.DB_USERTABLE.".".DB_USERTABLE_USERID." link,
cometchat_status.message, cometchat_status.status
from ".TABLE_PREFIX."friends join ".TABLE_PREFIX.DB_USERTABLE." on ".TABLE_PREFIX."friends.toid = ".TABLE_PREFIX.DB_USERTABLE.".".DB_USERTABLE_USERID." left join cometchat_status on ".TABLE_PREFIX.DB_USERTABLE.".".DB_USERTABLE_USERID." = cometchat_status.userid ".DB_AVATARTABLE."
where ".TABLE_PREFIX."friends.fromid = '".mysql_real_escape_string($userid)."' order by username asc");
return &sql;
My Rails app have friendships table :
friendships
user_id friend_id
id 1 2
id 2 1
When i just change order it does not work. I think that this is to hard for me right now. In rails i just check which friendship has a user_id of my profile and i show it friend_is as a friend.
THis is generated SQL for standard cometchat settings with friend table:
select DISTINCT users.id userid,
users.name username,
users.current_sign_in_at lastactivity,
users.id avatar,
users.id link,
cometchat_status.message,
cometchat_status.status
from friends
join users on friends.toid = users.id
left join cometchat_status on users.id = cometchat_status.userid
where friends.fromid = '10'
order by username asc
i try to change table from friends to my fiendships and toid (on friend_id) and formid( on user_id) and and vice versa but still it doesnt work and i do not get friends list.

basic join on mysql

I am having a hard time understanding joins on mySQL, and I cannot find any similar example to work with.
Suppose I have two tables: users and users_info.
in users I have id, email and password fields while, in users_info I have all their information, like name, surname, street, etc.
so, if I am getting a user like this:
SELECT * FROM users WHERE id = 43
and their information like this:
SELECT * FROM users_info WHERE id = 43
I will basically get 2 results, and 2 tables.
I understand now that I need to use join so that they are all together, but I just can't figure out out.
Any help?
It seems like both tables users and user_info are related with each others by the column id therefore you need to join them using this column like this:
SELECT
u.id,
u.email,
u.password,
i.name,
i.surname,
i.street
FROM users AS u
INNER JOIN user_info AS i ON u.id = i.id;
This will only select the fields id, email, ... etc. However, if you want to select all the columns from both the tables use SELECT *:
SELECT *
FROM users AS u
INNER JOIN user_info AS i ON u.id = i.id;
If you want to input the id and get all of these data for a specific user, add a WHERE clause at the end of the query:
SELECT *
FROM users AS u
INNER JOIN user_info AS i ON u.id = i.id
WHERE u.id = 43;
For more information about JOIN kindly see the following:
Join (SQL)From Wikipedia.
Visual Representation of SQL Joins.
Another Visual Explanation of SQL Joins.
Here's an example
SELECT * FROM users u
INNER JOIN users_info i
ON u.id=i.id
this means, you are joining users table and users_info table
for example
users
id name
---- -------
1 abc
2 xyz
users_info
id email
--- ------
1 abc#aaa.com
2 xyz#aaa.com
the query will return
id name email
--- ----- -------
1 abc abc#aaa.com
2 xyz xyz#aaa.com
Here's a nice tutorial
You can also do:
SELECT users.*, users_info.*
FROM users, users_info
WHERE users.id = users_info.id AND users.id = 43;
This means:
"Get me all the columns from the users table, all the columns from the users_info table for the lines where the id column of users and the id column of users_info correspond to each other"