I have four tables at the moment, users
, user_groups
, user_group_memberships
and user_group_permissions
. user_group_memberships
is used to link a users.id
to a user_groups.user_id
and user_group_permissions
is used to link members of a group to a list of permissions/rights.
I want to run a single query that gets an array of all groups from user_groups, and in the same query get the number of members in each group from user_group_memberships, then I would like to get the name of the user policy from user_group_permissions.
The query 'works' if every user_groups
has members in user_group_memberhips
and if every user_groups
record has a policy set in user_group_permissions
., but this will not return any groups that do not yet have member or a user policy assigned. Am I misunderstanding the handling if NULL
or my JOIN?
SELECT ug.*,
(SELECT count(*) FROM user_group_memberships WHERE ug.id = ugm.group_id) AS member_count,
(SELECT policy_name FROM user_group_permissions WHERE ugp.id = ug.user_policy_id) AS policy_name
FROM
user_groups AS ug
LEFT JOIN
user_group_memberships AS ugm ON ug.id = ugm.group_id
LEFT JOIN
user_group_permissions AS ugp ON ug.user_policy_id = ugp.id
WHERE
ug.organisation_id=?
users
+----+-----------------+
| id | username |
+----+-----------------+
| 1 | Thomas |
| 2 | Harry |
+----+-----------------+
user_groups
+----+-----------------+-------------------+------------+
| id | organisation_id | user_permission_id| group_name |
+----+-----------------+-------------------+------------+
| 1 | 123 | 1 | Finance |
| 2 | 123 | 2 | Support |
+----+-----------------+-------------------+------------+
user_group_memberships
+----+-----------------+----------+----------+
| id | organisation_id | user_id | group_id |
+----+-----------------+----------+----------+
| 1 | 123 | 1 | 1 |
| 2 | 123 | 2 | 1 |
+----+-----------------+----------+----------+
user_group_permissions
+----+-----------------+
| id | policy_name |
+----+-----------------+
| 1 | Finance |
| 2 | Support |
+----+-----------------+
Using the example above, I would expect my query to return two rows (one for each group), with member_count = 2
for group 1 (row 1) and member_count = 0
for group 2 (row 2). Currently it only returns a single for group 1 as member_count
exists/is not null. It returns no data for group 2 as there are no records for group 2 in group_memberships
to satisfy the COUNT().
The same issues occurs when user_groups.user_permissions_id
is NULL, it will only return a group record if the group has members and if the group has user_permission_id set.
Your usage of Correlated subqueries is wrong. Also, to get the count of members, you dont need to use Subquery; you can use Group by
with Count()
.
Try:
SELECT ug.id,
ug.organisation_id,
ug.group_name,
COUNT(ugm.group_id) AS member_count,
ugp.policy_name
FROM
user_groups AS ug
LEFT JOIN
user_group_memberships AS ugm ON ug.id = ugm.group_id
LEFT JOIN
user_group_permissions AS ugp ON ug.user_policy_id = ugp.id
WHERE
ug.organisation_id=?
GROUP BY
ug.id,
ug.organisation_id,
ug.group_name,
ugp.policy_name