Union two SELECT queries in one - mysql

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

Related

How to find percentage within a group in sql that has inner join as subquery?

I have 3 tables like this
create table Users (id serial primary key, country varchar(100) not null);
create table tweets(id serial primary key, user_id int, text varchar(100) not null, CONSTRAINT FK_TWEETSUSERS FOREIGN KEY (user_id)
REFERENCES Users(id));
create table Logins(user_id int, client varchar(100), CONSTRAINT FK_LOGIN_USERS FOREIGN KEY (user_id)
REFERENCES Users(id));
insert into Users
values
(1,'Japan'),
(2, 'Moroco'),
(3,'Japan'),
(4,'India'),
(5,'India'),
(6,'Japan'),
(7,'Moroco'),
(8,'China');
insert into tweets
values
(733,1,'I love #food'),
(734,1,'I love food'),
(735,2,'I love #food'),
(736,5,'I love food'),
(737,6,'I love #food'),
(738,3,'I love #food'),
(739,8,'I love #food');
insert into Logins
values
(1,'mobile-ios'),
(2,'mobile-ios'),
(3,'mobile-ios'),
(4,'web'),
(8,'mobile-ios');
I need to find percentage of users from each country whose users have used '#food' in their tweets and the other condition is that user should have logged in using 'mobile' device
I have written the following query so far -
select t.country, count(t.country) as tweet_users
from
(select Mobile_User_Tweets.user_id, U.country from Users as U
inner join
(select distinct user_id from tweets
where text like '%#food%'
and user_id in (select distinct user_id
from Logins
where client like '%mobile-%')) as Mobile_User_Tweets
on U.id = Mobile_User_Tweets.user_id) as t
group by t.country ;
This gives the number of users from a country that have user #food in their tweets
Result below -
country tweet_users
Japan 2
Moroco 1
China 1
I want the following result -
country tweet_users
Japan 66.67 -------------> (2 out of 3 users from Japan)
Moroco 50 -------------> (1 out of 2 users from Moroco)
China 100 -------------> (1 out of 1 user from China)
I tried number of different queries to find the percentage but haven't been able to get the result?
Can some one help me with this?
One way to achieve the results you want is to check in a derived table whether a user has made any tweets about #food; then you can LEFT JOIN that table to Users and Logins to determine the average number of users from each country that have logged in from mobile and tweeted about food:
SELECT u.country,
AVG(COALESCE(t.tfood, 0) AND COALESCE(l.client, '') LIKE '%mobile-%') * 100 AS tweet_users
FROM Users u
LEFT JOIN Logins l ON l.user_id = u.id
LEFT JOIN (
SELECT user_id, MAX(text LIKE '%#food%') AS tfood
FROM tweets
GROUP BY user_id
) t ON t.user_id = u.id
GROUP BY u.country
Output:
country tweet_users
China 100.0000
India 0.0000
Japan 66.6667
Moroco 50.0000
If you don't want countries with no users that meet the criteria, just add HAVING tweet_users > 0 to the end:
SELECT u.country,
AVG(COALESCE(t.tfood, 0) AND COALESCE(l.client, '') LIKE '%mobile-%') * 100 AS tweet_users
FROM Users u
LEFT JOIN Logins l ON l.user_id = u.id
LEFT JOIN (
SELECT user_id, MAX(text LIKE '%#food%') AS tfood
FROM tweets
GROUP BY user_id
) t ON t.user_id = u.id
GROUP BY u.country
HAVING tweet_users > 0
Demo on dbfiddle
Note this code takes advantage of the fact that in a numeric context, MySQL treats boolean expressions as 1 (true) or 0 (false).
Note that if a user might have multiple entries in the Logins table, you need to make a derived table from that too:
SELECT u.country,
AVG(COALESCE(t.tfood, 0) AND COALESCE(l.mclient, 0)) * 100 AS tweet_users
FROM Users u
LEFT JOIN (
SELECT user_id, MAX(client LIKE '%mobile-%') AS mclient
FROM Logins
GROUP BY user_id
) l ON l.user_id = u.id
LEFT JOIN (
SELECT user_id, MAX(text LIKE '%#food%') AS tfood
FROM tweets
GROUP BY user_id
) t ON t.user_id = u.id
GROUP BY u.country
Demo on dbfiddle

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"

Select only one row from a join based on foreign condition

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")

How to select users without specific one to many rows in MySQL

Consider the following data set:
users table:
id (int) email (string)
1 first#example.com
2 second#example.com
order_items table:
id (int) user_id (int) generation (string)
1 1 '11'
2 1 '12'
2 1 '12.50'
3 1 '16.00'
4 2 '11'
5 2 '12'
UPDATED question
How can I select users which doesn't have order_items with generation 16.00 and have at least one order_item?
So:
email
second#example.com
1) Returning Users who don't have order item with generation 16 included users with no orders at all.
Assuming you have some kind of id column in order_items table:
select u.* from users u
left outer join order_items oi on (u.id = oi.user_id and oi.generation = 16)
where oi.id is null;
Otherwise use whatever primary key you have in order_items in the where condition to be NULL.
Updated to include answer for the question in comment
2) Returning users who don't have order item with generation 16 but have least one order.
select distinct u.* from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null;
We do the filtering by using a second (normal) join which only returns users where it finds matching rows from the order_items table.
Here we need the distinct because the second join will multiply your rows depending on how many other orders the user have.
Alternatively you can also do a count or sum like this:
select u.*, count(distinct oiother.id) from users u
left outer join order_items oi16 on (u.id = oi.user_id and oi.generation = 16)
join order_items oiother on (u.id = oiother.user_id and oiother.generation != 16)
where oi16.id is null
group by u.id;
This will give you also how many other order items each returned user have. Or omit the count completely and using group by just to return distinct items.
You can use NOT EXISTS() like this:
SELECT * FROM Users u
WHERE NOT EXISTS(SELECT 1 FROM order_items o
WHERE o.userid = u.id
AND o.generation = 16)
That checks if there is a record for this user with order.generation = 16, and if there isn't it selects him.
Or not in()
SELECT * FROM Users u
WHERE u.id NOT IN(SELECT userid FROM order_items o
WHERE o.generation = 16)
That selects the list of users who have order.generation = 16, and select every id except them.
Following query should give you the desired output:
*update*
changed query as per the new result format in the question
As we want the data only from generation table, join with user table is not needed anymore. Here's the updated query:
select id, generation
from mytable where id not in (
select id from mytable
where generation = 16
group by id
);
Here is the SQL fiddle for it.

Using select statement with two tables

I have two tables. One contains User and company relationship a show below
User_company
UserId CompanyId
1 2
2 1
3 1
4 2
Another table holds user information
User
Id Name City
1 Peter LA
2 Harry SF
3 John NY
4 Joe CI
How do I make a statement which will give me All the users which are in company 1? Will something like
Select * from User where Id in (Select UserId from User_company where CompanyId = 1)
work?
SELECT * from User
left join User_company on User_company.UserId=User.Id
This would work...
SELECT * works but can be sluggish over time as it may not scale well with more data.
FROM User
WHERE Id in (Select UserId from User_company where CompanyId = 1)
So would this.. - best if you need data from both tables.
SELECT *
FROM User U
INNER JOIN User_Company UC
ON U.ID = UC.UserID
WHERE UC.CompanyID = 1
As would this - Probably the fastest if you just need data from user table.
Select * from User U
where exists (Select * from User_Company UC where U.ID = UC.UserID and CompanyID = 1)
OUTER joins are only needed if you need all records from one table and only those that match in another.
As to which is the best above: it depends on existing indexes and other requirements. Any of the above will return what's been asked for.
Try this
Select u.*
from User u
inner join User_company uc
on u.Id = uc.UserId
and uc.CompanyId = 1
BTW, what's wrong with the query you have posted? It will work as well fine. Just that it's a subquery and you better replace it with Join for performance.
Select * from User where Id in
(Select UserId from User_company where CompanyId = 1)
SELECT U.* FROM User AS U LEFT JOIN
User_company AS UC ON U.Id = UC.UserId WHERE UC.CompanyId = 1