Union and Intersection of data - mysql

I have three tables:
user
id | name
------------------
1 | Foo
2 | Bar
3 | Baz
group_type
id | name
------------------
1 | Group 1
2 | Group 2
3 | Group 3
4 | Group 4
5 | Group 5
user_group
id | user_id | group_type_id | [..]
------------------------------------
1 | 1 | 1 | [..]
2 | 1 | 3 | [..]
3 | 2 | 1 | [..]
4 | 1 | 5 | [..]
5 | 2 | 3 | [..]
6 | 3 | 3 | [..]
Well, currently, I can find all users from a specified list of groups with union, which is like a "or" clause:
SELECT u.*
FROM user u,
user_group ug
WHERE ug.user_id = u.id
AND ug.group_type_id IN( 1, 3, 5 )
Resulting:
id | name
------------------
1 | Foo
2 | Bar
3 | Baz
Now, I need to intersect the gorup, find all users which have groups of type 1 AND 3, resulting:
id | name
------------------
1 | Foo
2 | Bar
I have tried some queries, but don't imagine a way of doing this correctly.

SELECT u.id, u.name
FROM user u
INNER JOIN user_group g
ON u.id = g.user_id
WHERE ug.group_type_id IN (1,3)
GROUP BY u.id, u.name
HAVING count(distinct ug.group_type_id) = 2
Not as clean as the normal case, but it's certainly possible.

Try to use INTERSECT query. The syntax for the SQL INTERSECT query is:
select field1, field2, ... field_n
from tabl,tab2...
INTERSECT
select field1, field2, ... field_n
from tablel,table2...

I'm not sure if my syntax is perfect, but I'd reccomend self-joining user_group onto itself using user_id and forcing one of the selected entries (ug1 and ug2) to have ug1.group_type_id=1 and the other ug2.group_type_id=3. This gives you all user_id's with 1 AND 3 as their group_type_id. Now that you have that, you can do another join onto your user table, giving you all of the results that you were looking for.
SELECT u.*
FROM user u
JOIN (SELECT ug1.user_id
FROM user_group ug1 JOIN user_group ug2
ON ug1.user_id=ug2.user_id
WHERE ug1.group_type_id=1 and ug2.group_type_id=3) ug
ON u.id=ug.user_id

Related

How to create right query?

I'm trying write a query:
SELECT id FROM users WHERE status = 3
But if this sample returns an empty response, then I need instead to select the id where status = 4, and if it returns empty again, where status = 5.
How can I write a single query to solve this?
I think you simply want:
SELECT id
FROM users
WHERE status >= 3
ORDER BY status asc
LIMIT 1;
If you want multiple users:
SELECT u.id
FROM users u
WHERE u.status = (SELECT MIN(u2.status)
FROM users u2
WHERE u2.status >= 3
);
If you have a fixed list you want to test, you can also use:
select u.id
from users u
where u.status = 3
union all
select u.id
from users u
where u.status = 4 and
not exists (select 1 from users u2 where u2.status in (3))
union all
select u.id
from users u
where u.status = 5 and
not exists (select 1 from users u2 where u2.status in (3, 4));
You can use OR condition or use IN operator
SELECT id FROM users WHERE status = 3 or status = 3 or status = 5
or
SELECT id FROM users WHERE status IN (3,4,5)
I will use the case statement in the where clause:
select id
from users
where status = case when status = 3 and id is null then 4
when status = 4 and id is null then 5
else 3
end
Let me know if you have any question.
Assuming that your table look like this:
+----+--------+
| id | status |
+----+--------+
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 3 | 3 |
| 3 | 4 |
| 4 | 4 |
| 4 | 5 |
| 5 | 5 |
+----+--------+
And based on your condition where you want to see the lowest status first for each id, you can use MIN() operator.
So, from your original query:
SELECT id,MIN(status) FROM users GROUP BY id;
Then you'll get a result like this:
+----+-------------+
| id | MIN(status) |
+----+-------------+
| 1 | 3 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
+----+-------------+

How do I join multiple entries in one table into a single relationship status in MySQL?

I have a table Follow, which only holds records of which UserID follows which TargetID.
If asked for user A:
If neither A or B are following eachother, they have status of 0 for unrelated, and aren't included in the results.
If user A is following B but not vice versa, B has status 1 for
being followed.
If user B is following A but not vice versa, B has
status 2 for being a follower.
If A is following B, and B following
A, B has status of 3 for being a friend.
How can I, in a single MySQL query, get the relationship status for a given user and all its relationships above status 0?
Example:
Users:
+----+-------+
| id | Name |
+----+-------+
| 1 | Bob |
| 2 | Steve |
| 3 | Scott |
| 4 | Mary |
+----+-------+
Follow:
+----+--------+----------+
| id | UserID | TargetID |
+----+--------+----------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
| 4 | 4 | 1 |
+----+--------+----------+
Expected result for user 1:
+----------+--------+-------+
| TargetID | Status | Name |
+----------+--------+-------+
| 2 | 3 | Steve | (friend)
| 3 | 1 | Scott | (following)
| 4 | 2 | Mary | (follower)
+----------+--------+-------+
You can use subqueries as illustrated below:
-- FOR USER 1
SELECT A.id TargetID,
SUM(IFNULL((SELECT 1 C FROM Follow B WHERE B.UserID=1 AND B.TargetID=A.id),0) +
IFNULL((SELECT 2 C FROM Follow D WHERE A.id=D.UserID AND D.TargetID=1), 0)) Status
, A.name
FROM (SELECT * FROM Users WHERE ID<>1) A
GROUP BY A.id, A.Name
HAVING Status>0; -- for a compact result
-- NOW GLOBALLY
SELECT A.UserID, A.id TargetID,
SUM(IFNULL((SELECT 1 C FROM Follow B WHERE B.UserID=A.UserID AND B.TargetID=A.id),0) +
IFNULL((SELECT 2 C FROM Follow D WHERE A.id=D.UserID AND D.TargetID=A.UserID), 0)) Status
, A.name
FROM (SELECT E.id UserID, F.* FROM Users E JOIN Users F ON E.id<>F.id) A
GROUP BY A.UserID, A.id, A.Name
HAVING Status>0 -- for a compact result
ORDER BY A.UserID;
See DEMO on SQL Fiddle
I have not tried this but try something among the lines of:
Select t.targetid as TargetId,
IF (
(select count(id) from follow where
follow.Userid = f.target.id and follow.target_id = u.id) > 1,
-- mean’s the target is following user 1
(IF (
(select count(id) from follow where
follow.Userid = u.id and follow.target_id = f.targetid) > 1, 3, 2))
-- if user1 is following aswell, then its a friend, else its a follower
, 1)
-- else means its a following
as status,
u.name as Name from follow f
inner Join users u on u.id = f.targetid
where u.id = 1
Inner join to select user 1's relations (if it doesn't exist, they aren't related)
If there is a record, means they are one of 3:

Frequency join statement

I have 2 tables:
users:
id | name
-----------
1 | user 1
2 | user 2
3 | user 3
and posts:
id | userId | text
--------------------
1 | 1 | text 1
2 | 1 | text 2
3 | 2 | text 3
4 | 2 | text 4
5 | 2 | text 5
6 | 2 | text 6
I need to retrieve users ordered by post-frequency, e.g.:
id | name | posts
-------------------
2 | user 2 | 4
1 | user 1 | 1
3 | user 3 | 0
Some users might not have posts!
Currently I have 2 queries and doing it in 3 steps:
retrieve all users
retrieve all posts grouped by userId
use php to join the above
Question
Is the above possible to do in a single sql query?
select u.id, u.name, count(p.id) as posts
from users u
left join posts p on p.userid = u.id
group by u.id, u.name
order by posts desc
Another version, which prevents listing all fields from users table in group by clause. Also more fast in many cases IMO.
select u.id, u.name, coalesce(c.cnt, 0) as posts
from users u
left join
(select userId, couint(*) cnt from posts group by userId) c
on u.id = c.userId

JOIN and GROUP_CONCAT with three tables

I have three tables:
users: sports: user_sports:
id | name id | name id_user | id_sport | pref
---+-------- ---+------------ --------+----------+------
1 | Peter 1 | Tennis 1 | 1 | 0
2 | Alice 2 | Football 1 | 2 | 1
3 | Bob 3 | Basketball 2 | 3 | 0
3 | 1 | 2
3 | 3 | 1
3 | 2 | 0
The table user_sports links users and sports with an order of preference (pref).
I need to make a query that returns this:
id | name | sport_ids | sport_names
---+-------+-----------+----------------------------
1 | Peter | 1,2 | Tennis,Football
2 | Alice | 3 | Basketball
3 | Bob | 2,3,1 | Football,Basketball,Tennis
I have tried with JOIN and GROUP_CONCAT but I get weird results.
Do I need to do a nested query?
Any ideas?
Its not particularly difficult.
Join the three tables using the JOIN clause.
Use Group_concat on the fields you're interested in.
Don't forget the GROUP BY clause on the fields you're not concatenating or weird things will happen
SELECT u.id,
u.Name,
Group_concat(us.id_sport order by pref) sport_ids,
Group_concat(s.name order by pref) sport_names
FROM users u
LEFT JOIN User_Sports us
ON u.id = us.id_user
LEFT JOIN sports s
ON US.id_sport = s.id
GROUP BY u.id,
u.Name
DEMO
Update LEFT JOIN for when the user doesn't have entries in User_Sports as per comments
I think this is just a simple join and aggregation:
select u.id, u.name, group_concat(s.name order by pref separator ',')
from user_sports us join
users u
on us.id_user = u.id join
sports s
on us.id_sport = s.id
group by u.id, u.name

Joining tables when certain one table has no values?

I have these tables:
USER TABLE
uid | name | role
| |
1 | bob | package 1
2 | jill | package 2
3 | pam | package 1
NODE TABLE
nid | uid | type
| |
1 | 1 | car
2 | 1 | car
3 | 1 | car
4 | 2 | page
5 | 1 | car
6 | 3 | car
If I do:
select u.uid, u.name, count(nid) as totalNodes from USER as u left join NODE on n.uid = u.uid where n.type = 'car' group by u.uid
I end up with:
uid | name | totalNodes
| |
1 | bob | 4
3 | pam | 1
In other words, Jill is excluded. Why? And how can I avoid this? I.e. I want Jill to also appear in the list, but with totalNodes as 0 (or even NULL).
You need to perform your aggregate before attempting to join the tables as what you are currently doing is left joining, then restricting the data (at which point Jill is excluded) then grouping. If you do the count and restriction in a subquery you can then left join these results to the user table for the output you want:
SELECT u.uid, u.name, IFNULL(c.nodecount,0) AS `count`
FROM USER u LEFT JOIN (
SELECT uid, `type` , COUNT(nid) AS nodecount
FROM node
WHERE TYPE = 'car'
GROUP BY uid, type
) AS c ON u.uid = c.uid
use RIGHT JOIN instead left,
try :
select u.uid, u.name, count(nid) as totalNodes from USER as u
right join NODE on n.uid = u.uid where n.type IS NULL or n.type = 'car' group by n.uid
see this excellent post a visual explanation of joins :
http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
mysql syntax of join :
http://dev.mysql.com/doc/refman/5.0/en/join.html