MySQL: Get all users not a part of a group - php

I'm trying to get my head around this query but one of the records still keeps popping up. In summary, my intention is to show group members on the left and names from a phonebook which are not included in the group on the right. That way the user can select them and add them to the group.
user_id firstname group_id grpname
------- --------- -------- ----------
1 Luker 3 Abc
2 John 1 Some Group
3 Sam 2 Awesome Group
4 Mitch 1 Some Group
4 Mitch 2 Awesome Group
5 Rocky (NULL) (NULL)
6 Pops (NULL) (NULL)
The unique thing is that if one of the users is a part of multiple groups (user_id 4), their name should not be shown in the phonebook at all since it would already be placed in the list of existing members.
-- Query for group_id 2
SELECT user.id user_id, user.firstname, grp.id group_id, grp.grpname FROM agi_user user
LEFT JOIN agi_group_user gu ON user.id = gu.user_id
LEFT JOIN agi_groups grp ON gu.group_id = grp.id
WHERE grp.id IS NULL OR grp.id != 2
GROUP BY user.id
but for some reason the user Mitch still keeps popping up.
user_id firstname group_id grpname
------- --------- -------- ----------
1 Luker 3 Abc
2 John 1 Some Group
4 Mitch 1 Some Group
5 Rocky (NULL) (NULL)
6 Paps (NULL) (NULL)
Edit:: The output I need is
user_id firstname group_id grpname
------- --------- -------- ----------
1 Luker 3 Abc
2 John 1 Some Group
5 Rocky (NULL) (NULL)
6 Paps (NULL) (NULL)
Basically, to I want to get all users not a part of that group including NULL. But since one user is a part of multiple groups, her record still comes up when it shouldn't because that other group probably has a group_id of 3 or 4 (or any other besides 2).

You can use NOT EXIST to check if there are no group/user rows at all for group 2 and the given user:
SELECT
user.id user_id,
user.firstname,
grp.id group_id,
grp.grpname
FROM
agi_user user
LEFT JOIN agi_group_user gu ON user.id = gu.user_id
LEFT JOIN agi_groups grp ON gu.group_id = grp.id
WHERE
NOT EXISTS (
SELECT 'x' FROM agi_group_user x
WHERE
x.user_id = user.user_id and
x.group_id = 2)
GROUP BY
user.id
Note that if a user is not in group 2, but is in two other groups, he will show up 1 time in this result and only one of the groups he is in is returned. To fix that, you can add grp.id to the GROUP BY clause, or maybe you can use GROUP_CONCAT to return a list of group names in a single field.
Alternatively, this should work too, and in MySQL it might even perform better since it sucks at subqueries. Personally I think it is semantically less clear what is going on, though.
It joins the user/groups table a second time, but adds the group_id (2) to the join. If there are no rows returned for this table, then the user is not in group 2. The comment regarding GROUP_CONCAT applies to this query as well.
SELECT
user.id user_id,
user.firstname,
grp.id group_id,
grp.grpname
FROM
agi_user user
LEFT JOIN agi_group_user gu ON user.id = gu.user_id
LEFT JOIN agi_groups grp ON gu.group_id = grp.id
LEFT JOIN agi_group_user x ON user.id = x.user_id and x.group_id = 2
WHERE
x.group_id IS NULL
GROUP BY
user.id

If I understood correctly, you want this: "All users that are not in group 2"
This is called an anti-semijoin (or just anti-join) and can be done with a LEFT JOIN /IS NULL query:
SELECT u.id user_id, u.firstname
FROM agi_user AS u
LEFT JOIN agi_group_user AS gu
ON u.id = gu.user_id
AND gu.group_id = 2
WHERE gu.group_id IS NULL ;
or with a NOT EXISTS subquery:
SELECT u.id user_id, u.firstname
FROM agi_user AS u
WHERE NOT EXISTS
( SELECT 1
FROM JOIN agi_group_user AS gu
WHERE u.id = gu.user_id
AND gu.group_id = 2
) ;

Related

Or condition in INNER JOIN

I have a table structured as follows (points):
id1 | id2 | p1 | p2
1 3 5 7
3 1 2 4
1 2 1 7
And another table strucuted as follows (users):
id | name
1 User1
2 User2
3 User3
So now, I need a query that specifing an ID (for example 3), the query check that the ID appears in the column id1 and id2, and if it appears in one of the two columns, it gives me back the user name with id1 and id2 from the rows selected. So, for example if I specific the ID 3, the query give me back:
name1 | name2 | p1 | p2
User1 User3 5 7
User3 User1 2 4
I tried various solutions but no way to do it, I think that I need an OR condition in the INNER JOIN but I don't know if it's possible and if it's the solution.. I didn't find nothing here.
I mean something like:
INNER JOIN users ON (users.id = points.id1) || (users.id = points.id2)
Any solution for that? Thanks
Join the user table twice:
SELECT u1.name, u2.name, p.p1, p.p2
FROM points p
JOIN users u1 ON u1.id = p.id1
JOIN users u2 ON u2.id = p.id2
WHERE u1.id = 3 OR u2.id = 3
Use case statement it will give you all matching value not need restricted for one or two values
CREATE TABLE points (id1 int(2), id2 int(2), p1 int(2), p2 int(2));
INSERT INTO points VALUES(1,3,5,7);
INSERT INTO points VALUES(3,1,2,4);
INSERT INTO points VALUES(1,2,1,7);
CREATE TABLE users (id int(2), name char(23));
INSERT INTO users VALUES(1,'user1');
INSERT INTO users VALUES(2,'user2');
INSERT INTO users VALUES(3,'user3');
SELECT (CASE WHEN u.id = p.id1 THEN u.name END) AS name1,
(CASE WHEN u1.id = p.id2 THEN u1.name END) AS name2,
p1, p2
FROM points p
INNER JOIN users u ON (u.id = p.id1)
INNER JOIN users u1 ON (u1.id = p.id2);

SQL: Get null as a return value for rows that don't match query

I have a table, Entity and a table Friends. I want to get the names of people who have visited the same location, but I only want to return them if they are not friends with the person (suggested friend query). In order to do this I have written the following query:
SELECT Entity_Id,
Category AS FC
FROM entity
LEFT
JOIN friends
ON entity.Entity_Id = friends.Entity_Id1
OR entity.Entity_Id = friends.Entity_Id2
WHERE Entity_Id IN ( :list_of_ids )
AND Entity_Id != :user_id
AND Category != 4
GROUP
BY Entity_Id
:list_of_ids is a comma separated list of user id's and in my test query there are 82 users, however only 15 users are returned from the query, where the users returned are users who have a relationship with :user_id.
Any user who does not have a relationship in the table is not returned in the query. I thought by providing a LEFT OUTER JOIN it would return NULL for fields that were not found in the friends table.
----- EXPECTED -----
--------------------
Entity_Id FC
--------------------
1 3
2 2
3 2
4 null
52 null
64 null
------ ACTUAL -------
---------------------
Entity_Id FC
---------------------
1 3
2 2
3 2
The structure of my friends table is as follows, also note that it supports reciprocal relationships if that is any help...
------ FRIENDS ------
---------------------
fid PK
entity_id1 INT
entity_id2 INT
category INT -- 1 = Facebook, 2 = G+, 3 = App, 4 = Blocked
How can I return the users who have a missing relationship?
You want a left outer join. But, with a left join, the conditions on the second table need to go into the on clause:
SELECT e.Entity_Id, f.Category AS FC
FROM entity e LEFT JOIN
friends f
ON e.Entity_Id IN (f.Entity_Id1, f.Entity_Id2) AND
f.category <> 4
WHERE e.Entity_Id IN ( :list_of_ids ) AND
e.Entity_Id <> :user_id
GROUP BY e.Entity_Id;
When you have the condition on category in the WHERE clause, you turn the LEFT JOIN into an INNER JOIN. When there is no match, category has a value of NULL, which fails the comparison.
Which table does the Category belong to? If its a friends column, move Category != 4 from WHERE to ON to get true outer join. (Otherwise it executes as a regular innner join...):
SELECT Entity_Id, Category AS FC
FROM entity
LEFT JOIN friends
ON (entity.Entity_Id = friends.Entity_Id1
OR entity.Entity_Id = friends.Entity_Id2)
AND Category != 4
WHERE entity.Entity_Id IN ( :list_of_ids )
AND entity.Entity_Id != :user_id
GROUP
BY Entity_Id

mysql multiple COUNT() from multiple tables with LEFT JOIN

I want to show the conclusion of all users.
I have 3 tables.
table post
post_id(index) user_id
1 1
2 3
3 3
4 4
table photo
photo_id(index) user_id
1 2
2 4
3 1
4 1
table video
photo_id(index) user_id
1 4
2 4
3 3
4 3
and in table user
user_id(index) user_name
1 mark
2 tommy
3 john
4 james
in fact, it has more than 4 rows for every tables.
I want the result like this.
id name post photo videos
1 mark 1 2 0
2 tommy 0 1 0
3 john 2 0 2
4 james 1 1 2
5 .. .. .. ..
Code below is SQL that can work correctly but very slow, I will be true appreciated if you help me how it using LEFT JOIN for it. Thanks.
SQL
"select user.*,
(select count(*) from post where post.userid = user.userid) postCount,
(select count(*) from photo where photo.userid = user.userid) photoCount,
(select count(*) from video where video .userid = user.userid) videoCount
from user order by user.id"
(or ORDER BY postCount, photoCount or videoCount ASC or DESC as i want )
I done researched before but no any helped me.
SELECT u.user_id,
u.user_name,
COUNT(DISTINCT p.post_id) AS `postCount`,
COUNT(DISTINCT ph.photo_id) AS `photoCount`,
COUNT(DISTINCT v.video_id) AS `videoCount`
FROM user u
LEFT JOIN post p
ON p.user_id = u.user_id
LEFT JOIN photo ph
ON ph.user_id = u.user_id
LEFT JOIN video v
ON v.user_id = u.user_id
GROUP BY u.user_id
ORDER BY postCount;
Live DEMO
Your method of doing this is quite reasonable. Here is your query:
select user.*,
(select count(*) from post where post.userid = user.userid) as postCount,
(select count(*) from photo where photo.userid = user.userid) as photoCount,
(select count(*) from video where video.userid = user.userid) as videoCount
from user
order by user.id;
For this query, you want the following indexes:
post(userid)
photo(userid)
video(userid)
user(id)
You probably already have the last one, because user.id is probably the primary key of the table.
Note that a left join approach is a bad idea in this case. The three tables -- posts, photos, and videos -- are independent of each other. If a user has five of each, then joining them together would produce 125 intermediate rows. If a user has fifty of each, it would be 125,000 -- a lot of extra processing.
Your answer is probably slow as it is using a correlated sub-query i.e. the sub query is running once for each user_id (unless the optimizer is doing something smart - which shouldn't be counted on).
You could use a left outer join and count or use something temporary like:
SELECT u.user_id,
u.user_name,
ph.user_count AS 'photoCount',
p.user_count AS 'postCount',
v.user_count AS 'videoCount'
FROM user u
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM photo
GROUP BY user_id
) ph
ON ph.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM post
GROUP BY user_id
) p
ON p.user_id=u.user_id
INNER JOIN ( SELECT user_id,
COUNT(*) AS user_count
FROM video
GROUP BY user_id
) v
ON v.user_id=u.user_id
There are pros and cons for both (depending on indexes). Always have a look at the query plan (using EXPLAIN for MySQL).

Counting rows from second table

I have two tables in mysql database
groups
id|name
_______
1 |red
2 |blue
3 |green
4 |white
and users
id|name |group
_______________
1 |joe |1
2 |max |1
3 |anna |2
4 |lisa |2
So... joe and max are in the "red" group, anna and lisa are in the "blue" group.
How can I make simple listing of groups which would contain the number of
persons in that group
For example
red - 2
blue - 2
green - 0
white - 0
Most the other answers are basically correct, but forgot an important detail: GROUP is a reserved word in SQL, so your column name must be escaped:
SELECT groups.name, COUNT(*) AS total_members
FROM groups
LEFT OUTER JOIN users
ON users.`group` = groups.id
GROUP BY groups.id
check if this works....
SELECT COUNT(*), groups.name FROM groups, users WHERE users.group=groups.id GROUP BY groups.name
UPDATE
SELECT groups.name, COUNT(users.*) FROM groups LEFT JOIN users
ON groups.id=users.group GROUP BY groups.name
this will keep the colors even if they dont have any name related
Give this a try:
select g.name, count(u.id) from groups g
left join users u on g.id = u.group
group by g.id, g.name
Try this
SELECT COUNT(g.id) as count, g.name
FROM groups as g
LEFT JOIN users as u
ON u.group = g.id
GROUP BY g.id
This should work
SELECT g.*, COUNT(DISTINCT u.id) FROM `groups` g
INNER JOIN `users` u on g.id = u.group
GROUP BY u.id
maybe this should work
SELECT g.name, COUNT( u.id ) AS Totoal
FROM `groups` g
INNER JOIN `users` u ON g.id = u.group
GROUP BY g.id

Mysql left-join, count by unique value in a field not working

I have a table like below.
I want to be able to get a count of group_id then group by group_id then left join groups table where groups_user.group_id = groups.id but I'm only getting one result back.
I want group by unique group_id then count of each duplicate group_id. So far my query is like below:
SELECT 'groups.group' ,COUNT('groups_users.group_id') as groups_count
FROM `groups_users`
LEFT JOIN groups
ON 'groups_users.group_id' = 'groups.id'
GROUP BY 'groups_users.group_id'
Table:
id group_id user_id
26 3 1
22 2 1
19 1 1
20 1 2
21 1 4
Where am I getting wrong here?
I think you've got the JOIN backwards. Try:
SELECT g.group, COUNT(gu.group_id) AS groups_count
FROM groups g
LEFT JOIN groups_users gu
ON g.id = gu.group_id
GROUP BY g.group;

Categories