Find minimum value with subquery in HAVING clause - mysql

I need to answer the question:
"In a system with roles and permissions, what is the lowest ranking role that has a given permission?"
I'm trying to use a subquery in the HAVING clause but any other way to answer the question is fine.
There are three tables involved: permissions, roles, and permission_roles. A simplified structure is below.
PERMISSIONS
id
permission_name
1
View Customer
2
Edit Customer
3
Create Customer
ROLES
id
role_name
role_level
101
agent
25
102
manager
50
103
executive
75
PERMISSION_ROLE
id
role_id
permission_id
1001
101
1
1002
102
1
1003
102
2
1004
103
1
1005
103
2
1006
103
3
This is the result I am trying to produce, given the data above:
EXPECTED RESULTS
Permission ID
Permission
Role Name
Lowest Role Level
1
View Customer
agent
25
2
Edit Customer
manager
50
3
Create Customer
executive
75
The query below gives me the level for each role, but I want to list only the role(ss) with the lowest role_level - 1 result for each permission, showing the role(s) with the lowest role_level value for that permission. I'm trying to use the HAVING clause without success. Any help appreciated.
SELECT
`permissions`.`id` `Permissions ID`,
`permissions`.`permission_name` `Permission`,
`roles`.`role_name` `Role`,
`roles`.`role_level` `Role Level`
FROM
`permissions`
LEFT JOIN `permission_role` ON `permissions`.`id` = `permission_role`.`permission_id`
LEFT JOIN `roles` ON `roles`.`id` = `permission_role`.`role_id`
WHERE
GROUP BY
`permissions`.`id`
ORDER BY `permissions`.`id` ASC

You can try to use subquery to get min role_id each permission_id from permission_role table, then do JOIN
SELECT
p.id PermissionsID,
p.permission_name Permission,
r.role_name Role,
r.role_level RoleLevel
FROM
(
SELECT permission_id,min(role_id) role_id
FROM permission_role
GROUP BY permission_id
) pr
JOIN permissions p ON p.id = pr.permission_id
JOIN roles r ON r.id = pr.role_id
dbfiddle

Related

How to find the dependency that brought another dependency in MySQL or Redshift

There is two tables:
dependency_permission table:
id
dependency_permission_id
2
1
4
2
user_permission table:
id
user_id
permission_id
1
11111
1
2
22222
4
3
22222
2
4
11111
2
5
33333
2
I want to write a query that finds all user_id's which have permissions depend on another permission the user doesn't have.
from the above data, users (22222 and 33333) should be returned, they don't have permission 1 which 2 depends on.
You may left join the 'user_permission' table with the 'dependency_permission' to get the 'dependency_permission_id' for each user 'permission_id', then use NOT EXISTS operator to check the existence of 'dependency_permission_id' for each user 'permission_id'.
with user_dependency_permission as
(
select U.user_id, U.permission_id, D.dependency_permission_id
from user_permission U left join dependency_permission D
on U.permission_id = D.id
)
select user_id, permission_id /* if you want to select only user_ids use distinct user_id*/
from user_dependency_permission T
where not exists(
select 1 from user_dependency_permission D
where T.user_id=D.user_id and
D.permission_id=T.dependency_permission_id
)
and T.dependency_permission_id is not null
See a demo on MySQL.

Required SQL Query

I have been stuck on this for sometime now.
I have the following SQL Tables:
department table
Id Name
1 DeptA
2 DeptB
3 DeptC
users table
Id Name Dept
101 Alice 2
102 Bob 3
alpha table
Id Uid Title
501 101 HELLO
502 102 HEY
503 101 SQL
beta table
Id Uid Title
601 101 HELLO1
602 101 HEY1
603 102 SQL1
Explanation:
There's basically a users table which has all the users.
Each user has a department (Dept field)
Each user has some records, linked to it with Uid, in alpha and beta tables.
The result I want:
DeptA DeptB DeptC
0 4 2
I want the count of records in alpha and beta combined, grouped by Dept of the users whose records are there in these tables.
Can someone help me with the SQL query?
As per your table structure I've used dept id for retrieving result otherwise I used dept name. You can also use COALESCE function if you get NULL
-- MySQL
SELECT SUM(CASE WHEN d.id = 1 THEN COALESCE(total, 0) END) dept_A
, SUM(CASE WHEN d.id = 2 THEN COALESCE(total, 0) END) dept_B
, SUM(CASE WHEN d.id = 3 THEN COALESCE(total, 0) END) dept_C
FROM department d
LEFT JOIN (SELECT u.dept
, COUNT(1) total
FROM users u
INNER JOIN (SELECT uid
FROM alpha
UNION ALL
SELECT uid
FROM beta) t
ON u.id = t.uid
GROUP BY u.dept ) p
ON d.id = p.dept;
Please check url http://sqlfiddle.com/#!9/020b2/1

SQL query that produces the following output?

I am trying to write an SQL statement producing the below output.
I have the two following tables:
UserMovie
userID | movieID
-----------------
135 | k0jps
135 | p1zka
125 | v0t67
115 | opp2s
111 | xnwri
115 | kspdl
Follows
followerid | followingid
------------------------
122 | 135
192 | 111
125 | 240
120 | 125
45 | 111
I want to fetch the number of followers of each user who's userid is in the UserMovie Table, giving the following result:
Result
userid | followerCount
----------------------
135 | 1
125 | 1
115 | 0
111 | 2
The following statement gives me partially what i want:
SELECT followingid, count(*) as followerCount
FROM Follows
WHERE followingid in (SELECT DISTINCT userID FROM UserMovie)
GROUP BY followingid
The issue with the above query is that users with 0 followers do not appear in the results giving the following output:
userid | followerCount
----------------------
135 | 1
125 | 1
111 | 2
Any idea on how to do it?
Try this to include users with no follows:
SELECT UserId, Count(followerid) AS followerCount
FROM (SELECT DISTINCT userId FROM UserMovie ) m
LEFT JOIN Follows f
ON f.followingid = m.userID
GROUP BY UserId
Now it generates :
UserId followerCount
111 2
115 0
125 1
135 1
The following worked for me.
However I am getting NULLs instead of 0 for users with no followers
SELECT DISTINCT u.userid, t.followerCount
FROM UserMovie u
LEFT JOIN (
SELECT followingid, count(*) AS followerCount
FROM Follows
WHERE followingid in (SELECT DISTINCT userID FROM UserMovie)
GROUP BY followingid ) as t
on t.followingid = u.userid
How about a solution using CASE?
SELECT userId,
CASE
WHEN IFNULL(followerid, 0) = 0 THEN 0
ELSE count(*)
END
FROM UserMovie
LEFT JOIN Follows on followingid=userID
GROUP BY userId;
Seems to work fine in SQLite3, just replace IFNULL with ISNULL (if SQLServer) or any other equivalent. It's pretty similar to what you've done.
Here's one approach: get a distinct list of userID from UserMovie in an inline view (use either a GROUP BY or a DISTINCT keyword), and perform an "outer join" operation of that to the Followers table to find followers. Collapse the rows from that with a GROUP BY, and use an aggregate function to get a count of unique/distinct non-null values of userId from the Followers table.
For example:
SELECT u.userID
, COUNT(DISTINCT f.userID) AS cnt_followers
FROM ( SELECT m.userID
FROM UserMovie m
GROUP BY m.userID
) u
LEFT
JOIN Follows f
ON f.followingid = u.userID
GROUP BY u.userID
EDIT
There's an invalid column reference in the SELECT list, f.userID is not valid. That should be f.followerID.
When we fix that, the query returns:
userID cnt_followers
111 2
115 0
125 1
135 1
SQL Fiddle HERE http://sqlfiddle.com/#!9/de3e7/2
As long as we are counting "distinct" followerid (question doesn't give any guarantee that (followerID,followingID) is UNIQUE in Followers table), we could eliminate the inline view
SELECT u.userID
, COUNT(DISTINCT f.userID) AS cnt_followers
FROM UserMovie u
LEFT
JOIN Follows f
ON f.followingid = u.userID
GROUP BY u.userID

Calculate round(avg) from same table and join

I have two tables,
users
userid fname usertype
1 Steve vendor
2 Peter vendor
3 John normaluser
4 Mark normaluser
5 Kevin vendor
6 Alan vendor
7 Sean vendor
vendor_rating
id userid rating
1 1 4
2 2 3
3 2 2
4 2 4
5 1 3
6 5 2
7 5 2
userid is foreign key.
i want to show all vendors (only usertype vendor) from user table by descending/ascending average rating even if Vendor's rating is not available on table it should show, its information should display at last in descending, at first in ascending.
I want to fetch all users info from first table so i m using left join :
SELECT
users.name,
users.userid,
users.usertype
FROM users
LEFT JOIN (SELECT
ROUND(AVG(rating)) AS rating_avg,
userid
FROM vendor_rating
ORDER BY rating_avg DESC) ven
ON users.usertype = 'vendor'
AND users.userid = ven.userid
ORDER BY ven.rating_avg ASC;
Please help where am i going wrong.
EDIT:
I get this
userid ROUND(AVG(vr.ratings))
28 5
27 4
16 3
26 2
25 0
NULL NULL
NULL NULL
NULL NULL
NULL NULL
if i use
SELECT vr.userid, ROUND(AVG(vr.ratings)) FROM vendor_rating vr
RIGHT JOIN (SELECT users.fname, users.userid, users.usertype FROM users) u
ON u.id = vr.vendor_id WHERE u.usertype = 'vendor' GROUP BY vr.userid,u.fname
ORDER BY round(avg(vr.ratings)) ASC
i get NULL values from users table whose rating is not available in vendor_rating table those should display userids
Try to this
SELECT
vr.userid,
u.fname,
ROUND(AVG(vr.rating))
FROM vendor_rating vr
INNER JOIN users u
ON u.userid = vr.userid
WHERE u.usertype = 'vendor'
GROUP BY vr.userid,
u.fname
ORDER BY round(avg(vr.rating)) ASC
finally i got it
SELECT users.fname, users.userid,users.usertype
FROM users
LEFT JOIN (select ROUND(AVG (ratings)) AS rating_avg,userid FROM
vendor_rating group by userid order by rating_avg desc ) ven
ON users.id=userid
WHERE users.usertype='vendor'
order by rating_avg desc
Thank you all, for sharing views to get idea to solve my problem.

Count Memberships of User in Groups

I've a tree structure of an association which is divided in divisions, subdivisions etc. on every level users may have memberships to certain roles.
I want to count the memberships on every "structure type" (association, division, subdivision) as defined in the table
The Table structure looks like:
table intern_structures
Contains the hierarchy (nested set, but that does not matter here)
id | intern_structure_type_id | name | parent_id | lft | rgt
1 1 My Company USA 0 1 6
2 2 Texas 1 2 5
3 3 El Paso 2 3 4
table intern_structure_types
Contains Description to the types like "association", "division", "subdivision"
id | name
1 Association
2 Division
3 Subdivision
table memberships
Contains the memberships
id | user_id | intern_structure_id | role_id
1 1 1 1
2 1 2 2
3 2 3 1
3 2 3 3
....
table roles
Contains role descriptions
id | name
1 Admin
2 Moderator
3 Clerk
I want a grouped list like:
structure_type_name | role_name | count of memberships
Association Admin 1
Association Moderator 10
Association Clerk 0 << !! I miss the zero rows!
Division Admin 7
Divison Moderator 43
Division Clerk 31
Subdivision Admin 234
Subdivision Moderator 942
Subdivision Clerk 456
What I achieved so far is this query:
SELECT
is_types.name,
roles.name,
COUNT(memberships.id)
FROM
roles,
intern_structure_types AS is_types
LEFT JOIN intern_structures AS is_elements ON is_elements.intern_structure_type_id = is_types.id
LEFT JOIN memberships ON memberships.intern_structure_id = is_elements.id
WHERE
roles.id = memberships.role_id
GROUP BY
is_types.id, roles.id
It works fine except that it doesn't list all roles because some roles don't have any memberships yet but I want them listed as well just with 0 as membership count.
I'd be very thankful for any help!
I'm assuming the counts you showed in the OP are contrived. To get the results you want, you should create a derived table of the types and roles in use and then left join that entire query to a cross join of the roles and types.
Select is_types.name
, roles.name
, Count(Z.is_type_name)
From roles
Cross Join intern_structure_types As is_types
Left Join (
Select is_types.name As is_type_name
, roles.name As role_name
From intern_structures As is_elements
Join intern_structure_types As is_types
On is_types.id = is_elements.intern_structure_type_id
Join memberships
On memberships.intern_structure_id = is_elements.id
Join roles
On roles.id = memberships.role_id
) As Z
On Z.is_type_name = is_types.name
And Z.role_name = roles.name
Group By is_types.name, roles.name