Reputation: 336
I have three tables for users and users access rights as below:
create table users (id int, login varchar, organization varchar);
create table groups (id int, name varchar);
create table user_groups (gid int, uid int,
constraint fk_users_groups foreign key(gid) references groups(id),
constraint fk_users_users foreign key(uid) references users(id)
);
Lets insert some records:
insert into groups values(1, 'Data Entry'), (2, 'Sender'), (3, 'Receiver'),
(4, 'Reviewer'), (5, 'Checker'), (6, 'Approver'), (7, 'Nothing'), (8,
'Everything'), (9, 'HR Systems'), (10, 'Check System'), (11, 'Final');
insert into users (1, 'Ahmad'), (2, 'Sam'), (3, 'John'), (4, 'Smith'),
(5, 'Roy');
insert into user_groups values (1, 1), (2, 1), (3, 1), (8, 1), (9, 1), (4, 2),
(5, 2), (10, 2), (11, 2), (6, 3), (7, 4);
Now I want to fetch the user id, login and user type as such if the user ha ("data entry", "sender", "receiver") groups, then "HR" should be printed (we do not care for other groups if he has or not for ex: here he has the other groups as well rather than we need for the "HR" user type) for him as user type, if he/she is in ("reviewer", "checker") groups then "Check" should be printed else "Any" should be printed for any other groups.
The desired output is:
User ID Login UserType
1 Ahmad HR
2 Sam Check
3 John Any
4 Smith Any
Upvotes: 2
Views: 72
Reputation: 1269763
In Postgres, DISTINCT ON
might be the simplest approach:
SELECT DISTINCT ON (u.id) u.id AS user_id, u.login, g.userType
FROM users u JOIN
user_groups ug
ON u.id = ug.uid JOIN
(SELECT g.*,
(CASE WHEN g.name IN ('Data Entry', 'Sender', 'Receiver') THEN 'HR'
WHEN g.name IN ('Reviewer', 'Checker') THEN 'Check'
ELSE 'Any'
END) AS userType,
(CASE WHEN g.name IN ('Data Entry', 'Sender', 'Receiver') THEN 1
WHEN g.name IN ('Reviewer', 'Checker') THEN 2
ELSE 3
END) AS userType_priority
FROM groups g
) g
ON ug.gid = g.id
ORDER BY u.id, g.userType_priority;
Another alternative is CASE EXISTS
:
select u.*,
(case when exists (select 1
from user_groups u join
groups g
on ug.gid = g.id
where g.name in ('Data Entry', 'Sender', 'Receiver')
)
then 'HR'
when exists (select 1
from user_groups u join
groups g
on ug.gid = g.id
where g.name in ('Reviewer', 'Checker')
)
then 'Check'
else 'Any'
end) as UserType
from users u;
Despite the complicated look of the query, this involves no aggregation or sorting, so it could have the best performance.
I would recommend including the UserType
in the groups
table. That would simplify the query considerably -- and make any applications that use UserType
more consistent.
Upvotes: 0
Reputation: 12402
Add a column to the table groups that contains what should be printed, when you next need to add a new value to groups you will be glad you did!.
alter table groups add column usertype text; update groups set usertype = 'HR' where id between 1 and 3; update groups set usertype = 'Check' where id between 4 and 5;
SELECT DISTINCT u.id AS user_id, u.login,
COALESCE(g.usertype,'any')
FROM users u
JOIN user_groups ug ON u.id = ug.uid
LEFT OUTER JOIN groups g ON ug.gid= g.id and g.userype is not null
ORDER BY u.id;
this will print two lines for any user who has both HR
and Check
and one line for everyone else.
Upvotes: 0
Reputation: 238086
You could use max(case(
to find the top group someone is in. Here I've defined the order by prefixing the usertype with 1, 2 or 3:
select u.id
, login
, substring(max(case
when name in ('Data Entry', 'Sender', 'Receiver') THEN '3 HR'
when name in ('Reviewer', 'Checker') THEN '2 Check'
else '3 Any'
end) from 3)
from users u
left join
user_groups ug
on ug.uid = u.id
left join
groups g
on g.id = ug.gid
group by
u.id
, login
Upvotes: 1
Reputation: 175696
You could use:
SELECT DISTINCT u.id AS user_id, u.login,
CASE WHEN g.name IN ('Data Entry', 'Sender', 'Receiver') THEN 'HR'
WHEN g.name IN ('Reviewer', 'Checker') THEN 'Check'
ELSE 'Any'
END AS userType
FROM users u
JOIN user_groups ug ON u.id = ug.uid
JOIN groups g ON ug.gid= g.id
ORDER BY u.id;
Upvotes: 0