I have 5 tables like below
user
id name
11 rajesh
12 kumar
group
id name
21 sales
22 dev
domain
id name
51 network
52 database
user_group
user_id group_id
11 21
user_domain
user_id domain_id
12 51
I need to retrive all users who are member of atleast one group or domain. Here is the query that I am using to join two tables.
select user.name from user join user_domain on user.id=user_domain.user_id
I am able to join tables user and group. But along with that I need to join user and domain also. When I try like below I am getting zero results.
select user.name from user
left join user_group on user.id=user_group.user_id
join user_domain on user.id=user_domain.user_id
If I change to left join like below I am getting all users.
select user.name from user
left join user_group on user.id=group.user_id
left join user_domain on user.id=user_domain.user_id
Please advise
Isn't this enough?
SELECT `id`
FROM `user` JOIN `user_group` ON `user`.`id` = `user_group`.`user_id`
UNION
SELECT `id`
FROM `user` JOIN `user_domain` ON `user`.`id` = `user_domain`.`user_id`;
Related
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
I have four tables : one of users, one of profiles (users profiles), one of relations between profile and content and one of content. That content table could have no entry, single or multiple entry for a user profile.
I would like to get all the users who have no entry in the content table or if they have multiple entries, get only one.
Here is my current SQL :
SELECT
users.uid AS uid,
profile.id AS profile_id,
content.id AS content_id
FROM
users users_data
LEFT JOIN profile profile_data ON users_data.id = profile_data.uid
LEFT JOIN content_for_profile content_profile ON profile_data.id = content_profile.pid
LEFT JOIN content content_data ON content_for_profile.content_id = content_data.id
JOIN (
SELECT content_data_join.id, content_data_join.uid
FROM content content_data_join
GROUP BY content_data_join.uid
) content_data_join ON content_for_profile.content_id=content_data_join.id
GROUP BY uid, profile_id, content_id
ORDER BY profile.created DESC
I tried to add the simple JOIN to get only one result from the content table and it works. But with that, i don't get users with no entry in the content table.
I've been stuck for hours and try so many things ... Thanks in advance for your help !
EDIT :
user example
id
8
9
10
profile example :
id uid
2000 8
2001 9
2002 10
content_for_profile example :
id pid content_id
1 2000 100
2 2001 101
3 2001 102
content example :
id uid
100 8
101 9
102 9
expected result :
uid profile_id content_id
8 2000 100
9 2001 101
10 2002 NULL
for get the user without entry in content table you can use where condition for null
SELECT
users.uid AS uid,
profile.id AS profile_id,
content.id AS content_id
FROM
users users_data
LEFT JOIN profile profile_data ON users_data.id = profile_data.uid
LEFT JOIN content_for_profile content_profile ON profile_data.id = content_profile.pid
LEFT JOIN content content_data ON content_for_profile.content_id = content_data.id
where content_data.id is null
ORDER BY profile.created DESC
You should not use GROUP BY without aggreation function. this deprecated is most db and is not allowed in the most recent version of mysql
a
for get just one entry for content value the you can use an aggreation function eg min() ..max()
SELECT
users.uid AS uid,
profile.id AS profile_id,
min(content.id) AS content_id
FROM
users users_data
LEFT JOIN profile profile_data ON users_data.id = profile_data.uid
LEFT JOIN content_for_profile content_profile ON profile_data.id = content_profile.pid
LEFT JOIN content content_data ON content_for_profile.content_id = content_data.id
GROUP BY users.uid ,profile.id
ORDER BY profile.created DESC
I have a user table in the database where all users of the system are stored.
The table has a user_id and a business_name and a first_name.
Some users are merchants and get a business name,
some users are consumers and get a first name.
In a second table I have transactions with a user_id and a merchant_id (which are defining the transaction) and an amount. Both ids reference to user table.
Table users:
user_id bus_name first_name role_id
1 Thomas 10
2 comp1 7
3 Peter 10
4 comp2 7
(role_id is defining with 10=consumer, 7=merchant)
Table transactions:
trans_id amount user_id merchant_id
1 12 1 2
2 23 3 2
3 34 3 4
4 19 1 4
Now I want to have a query with a result as one table:
This table should contain the transaction with amount, user_id, first_name, merchant_id and bus_name.
I want to get this result:
trans_id amount user_id first_name merchant_id bus_name
1 12 1 Thomas 2 comp1
2 23 3 Peter 2 comp1
3 34 3 Peter 4 comp2
4 19 1 Thomas 4 comp2
I have the problem that either I get only the first_name and empty bus_name or I get only the bus_name but empty first_name.
I am using a left join:
...
left join `users`
on(
(`transactions`.`user_id` = `users`.`user_id`)
)
...
But for this I would get for user_id=1 the first_name=Thomas and the bus_name='' would be empty because I only reference to one line in table and not also to different user with user_id=2.
But I want to say something like:
for trans_id=1
get first_name FROM users WHERE transactions.user_id = users.user_id
AND
get bus_name FROM users WHERE transactions.merchant_id = users.user_id
Thanks for your help, I tried so many things but it does not work.
You have to join the user table twice:
SELECT t.*, u.first_name, m.bus_name
FROM transactions t
JOIN users as u
ON t.user_id = u.user_id
JOIN users as m
ON t.merchant_id = m.merchant_id
you could use a duoble join in users table
select a.trans_id, a.amount , a.user_id, b.first_name, a.merchant_id, c. bus_name
from transactions a
inner join users b on a.user_id = b.user_id and b.role_id = 10
inner join users c on a.merchant_id = c.user_id and c.role_id = 7
To join the user table twice worked fine. With "left join users as consumer" I create a kind of a virtual users table called "consumer", this one is joined. Of course in select I had to adjust table name as well. Same for second "virtual" table od users, called "merchant".
select
`transactions`.`trans_id` AS `trans_id`,
`transactions`.`merchant_id` AS `merchant_id`,
`merchant`.`bus_name` AS `bus_name`,
`transactions`.`user_id` AS `user_id`,
`consumer`.`first_name` AS `first_name`,
`cards`.`card_id` AS `card_id`,
`cards`.`serial_no` AS `serial_no`
from (
`transactions`
left join `cards`
on(
(`cards`.`card_id` = `transactions`.`card_id`)
)
left join `users` as consumer
on(
(`consumer`.`user_id` = `transactions`.`user_id`)
)
left join `users` as merchant
on(
(`merchant`.`user_id` = `transactions`.`merchant_id`)
)
)
I have three tables, payout, earnings, user. I try to get first the userid which I use to get earnings and payouts information about the user. With this I also count together with DISTINCT and substract it to get the CURRENT earnings. The problem is if any of the table has no entry example the payouts table so I get NULL instead of number. How can I solve this. I tried IFNULL but did not work
This is how I do
SELECT SUM(DISTINCT user_earnings.amount) -
SUM(DISTINCT user_payouts.payout_amount) as total_earnings
FROM user, user_earnings, user_payouts
WHERE user.id = 103
and user_earnings.user_id = user.id
and user_payouts.user_id = user.id
EDIT: I made the current tables
user_earnings ( the problem is just only with the user_id 103
id user_id amount
1 102 250
2 102 1000
3 101 5000
4 102 352
18 102 375
19 102 442
20 103 338 <-----
user_payouts
id user_id payout_amount
1 102 500
2 102 100
3 101 1000
user
id payout_address
102 ***
103 ***
As you see payouts have has no entry about user_id 103 because he never did a payout. Thats why I get null (I think)
Try this:
SELECT u.id,
(SUM(DISTINCT ue.amount) -
COALESCE(SUM(DISTINCT up.payout_amount),0)) as total_earnings
FROM
user u
left outer join user_earnings ue on u.id = ue.user_id
left outer join user_payouts up on u.id = up.user_id
WHERE
u.id = 103;
Sorry, I didn't put enough effort in to the above. While it will work for a small sample, the above breaks down with a larger set as you are filtering out any instance where a single user got two payouts of the same amount.
I think this is your real solution:
SELECT
u.id,
(ue.earnings - up.payouts) as total_earnings
FROM
user u
inner join
(select distinct
u1.id,
SUM(COALESCE(ue.amount,0)) as earnings
from
user u1
left join user_earnings ue on u1.id = ue.user_id group by u1.id) ue on u.id = ue.id
inner join
(select distinct
u1.id,
SUM(COALESCE(up.payout_amount,0)) as payouts
from
user u1
left join user_payouts up on u1.id = up.user_id group by u1.id) up on u.id = up.id
WHERE
u.id = 103;
Just for fun, here's another option, much shorter:
SELECT
u.id,
((select COALESCE(SUM(ue.amount),0) from user_earnings ue where ue.user_id = u.id) -
(select COALESCE(SUM(up.payout_amount),0) from user_payouts up where up.user_id = u.id)) as total_earnings
FROM
user u
WHERE
u.id = 103;
Note1:
I would definitely NOT use Distinct in this manner... If the user in the future has another earned_amount for 338, the second 338 would not get added to the total.
Note2:
See my usage of COALESCE instead of IFNULL.
Note3:
I updated your join syntax. Please confirm I kept your join as desired.
EDIT: Updated answer and fiddle to account for duplicate earned_amounts per user as well as duplicate payout_amounts per user
View Fiddle to notice duplicates of both entered for user 103 (400 + 400 - 100 - 100))
CLICK Here for SQLFiddle
SELECT (
COALESCE(ue.earned_amount,0) - COALESCE(up.payout_amount,0)
) AS total_earnings
FROM user u
JOIN (SELECT user_id, SUM(earned_amount) AS earned_amount
FROM Earned
GROUP BY user_id) ue
ON ue.user_id = u.id
JOIN (SELECT user_id, SUM(payout_amount) AS payout_amount
FROM Payouts
GROUP BY user_id) up
ON up.user_id = u.id
WHERE u.id = 103
Performance Note:
If your Earned or Payout tables are expected to be large a few ideas for performance improvement will be.
limit the size of the returned result sets of the nested queries by adding a Where clause to each so they only return results for user_id 103.
Add indexes to both Earned and Payout for the column user_id.
OR first create these joins to a temp table with indexes, then join the temp table to this query
You have 3 problems -- DISTINCT, JOIN followed by GROUP BY, and failure to deal with NULL.
SELECT u.id,
(
SELECT ( IFNULL(SUM(amount), 0)
FROM user_earnings WHERE user_id = u.id ) -
SELECT ( IFNULL(SUM(payout_amount), 0 )
FROM user_payouts WHERE user_id = u.id )
) as total_earnings
FROM user u
WHERE u.id = 103;
should solve all of them.
DISTINCT was already discussed -- there could be duplicate value for a given user.
JOIN followed by GROUP BY -- I call this "inflate-deflate syndrone". The JOIN happens first, thereby inflating the number of rows, then the GROUP BY tries to compensate for it. Usually this is a performance problem; in your case it mangles the data, too.
NULL was discussed, but I thing this formulation is 'correct'.
This is a 'rare' case where subqueries is better than LEFT JOIN. It avoids the over-calculation of SUM. ghenghy's first solution is probably just as good for a single u.id; mine should work for multiple ids.
For simplification, I have two tables related with one to many using a foreign key, for example:
Users table:
id
name
Actions table:
id
user_id
one user may have many actions or not. I need an sql select that returns users ids that don't have a user_id value in the actions table.
Users Table:
id name
1 John
2 Smith
3 Alice
Actions Table:
id user_id
1 3
2 1
So I need an sql query that returns the user id 2 (Smith) because the foreign key values don't include the id 2
I tried the following SQL, but it returns all users ids:
SELECT users.id from users left join actions on actions.user_id is null
select u.id
from users u
left outer join actions a on a.user_id = u.id
where a.user_id is null
Optimized version would be:
SELECT u.id
FROM users u
LEFT JOIN actions a
ON a.user_id = u.id
AND ISNULL(a.user_id)
SELECT u.id
FROM users u
LEFT JOIN actions a
ON a.user_id = u.id
WHERE a.user_id IS NULL