Required SQL Query - mysql

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

Related

Find minimum value with subquery in HAVING clause

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

SQL count asset inventory

In sql help i have 3 tables, table one is asset table which is as follow
id
asset_code
asset_name
asset_group
asset_quantity
1
A001
computer
4
7
2
A002
keyboard
6
4
and another table is asset_allocation
id
asset_id
allocated_quantity
allocated_location
returned
1
1
2
IT office
no
2
2
1
main hall
yes
the last table is asset_liquidated which will present assets that are no longer going to be used
id
asset_id
liquidated_quantity
1
1
1
Now lets say that i have 7 computer out of which 2 are allocated but not returned and i have 4 keyboards out of which 1 is allocated and it is returned back and 1 computer is liquidated means it is never going to be used
so now here i want to join these 3 tables and find inventory of my current stock in hand.
Now this is the query now i need to add this
where asset_allocation.returned is enum no inside this query
SELECT id,asset_code, asset_name, asset_group, asset_quantity,allocated_quantity,liquidated_quantity,
asset_quantity - COALESCE(AA.allocated_quantity, 0) - COALESCE(AL.liquidated_quantity, 0) available_quantity
FROM asset A
LEFT JOIN (SELECT asset_id, SUM(allocated_quantity) allocated_quantity
FROM asset_allocation
GROUP BY asset_id) AA ON A.id = AA.asset_id
LEFT JOIN (SELECT asset_id, SUM(liquidated_quantity) liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id) AL ON A.id = AL.asset_id;
I believe what you are looking for is adding WHERE returned = 'no' in your first JOIN like so:
SELECT id,asset_code, asset_name, asset_group, asset_quantity,allocated_quantity,liquidated_quantity,
asset_quantity - COALESCE(AA.allocated_quantity, 0) - COALESCE(AL.liquidated_quantity, 0) available_quantity
FROM asset A
LEFT JOIN (SELECT asset_id, SUM(allocated_quantity) allocated_quantity
FROM asset_allocation
WHERE returned = 'no'
GROUP BY asset_id) AA ON A.id = AA.asset_id
LEFT JOIN (SELECT asset_id, SUM(liquidated_quantity) liquidated_quantity
FROM asset_liquidated
GROUP BY asset_id) AL ON A.id = AL.asset_id;
That changes the available quantity for keyboard from 3 to 4 for me
your query:
vs. mine:

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

Complex SQL Union

Any SQL Guru's out there I could use some help! I am creating a stored procedure that I believe needs a Union so that all the results are brought back with 1 SELECT statement.
I have simplified my problem to the tables below:
user
user_id username name DOB
------------------------------------------------------
1 JohnSmith1 John Smith 01/01/1990
2 LisaGreen17 Lisa Green 03/07/1986
3 BarneyB Barney Brown 09/12/1960
user_team
user_team_id user_id team_id total_score
-------------------------------------------------------------
1 1 1 29
2 2 7 37
3 3 2 15
private_league
priv_league_id league_name host_user league_password
-------------------------------------------------------------------------
1 Lisa's League 2 CSUASH429d9
2 Barney's Bonanza 3 Jkap89f5I01
user_team_private_league_M2M
id priv_league_id user_team_id
----------------------------------------
1 1 1
2 1 2
3 1 3
4 2 1
5 2 3
I would like to run a stored procedure with an input of a user_id which will bring back all leagues entered by the user, the host of each of those leagues, how many total players have entered in each league and what position the user is in for each of those leagues(sorted by total score).
At the moment I have:
CREATE DEFINER=`root`#`localhost` PROCEDURE `user_private_leagues`(IN v_user_id INT)
BEGIN
DECLARE userteamid INT;
# Retrieve user team from a user_id
SELECT user_team_id INTO userteamid
FROM user_team
WHERE user_id = v_user_id;
# Retrieve private league name and host user (for a userteam)
SELECT private_league.league_name, private_league.host_user
FROM user_team_private_league_M2M
INNER JOIN privateleague
ON user_team_private_league_M2M.priv_league_id=private_league.priv_league_id
WHERE user_team_id = userteamid;
END
This query does not include the total number of players for each league and the current position of the user
I have created a query to bring back the total users for each private league, with no user filter like so:
SELECT private_league_id, COUNT(*) AS total_users
FROM classicseasonmodel_classicseasonuserteamprivateleague
GROUP BY private_league_id;
A query for the user's current position can be worked out by using the answer to this question and using total_score.
I am extremely stuck with this at the moment - the perfect result from the SP will be as follows:
CALL user_private_leagues(3); (user id of BarneyB)
priv_league_name current_position total_users host_user
-----------------------------------------------------------------------
Lisa's League 3 3 LisaGreen17
Barney's Bonanza 2 2 BarneyB
Thanks!
Sorry but I didn't create the DB to test the SQL below. But you can start from there. No need for UNION. I didn't understand the business rule to compute the user's position in the league, since it may come from the team or the user.
select priv_league_id, league_name, host_user_name, count(*) as total_users
from (
select A.priv_league_id, A.league_name, D.name as host_user_name, B.user_team_id, C.user_id, D.
from private_league A
join user_team_private_league_M2M B
on A.priv_league_id = B. priv_league_id
join user_team C
on B.user_team_id = C. user_team_id
join user D
on A.host_user = D.user_id
) D
group by priv_league_id
Let take it step by step....
First ID, Count for pleague
SELECT private_league_id, COUNT(*) AS total_users
FROM classicseasonmodel_classicseasonuserteamprivateleague
GROUP BY private_league_id;
Now add in Name and host user
SELECT PL.league_name, LC.uCNT, PL.host_user
FROM (SELECT private_league_id AS pID, COUNT(*) AS uCNT
FROM classicseasonmodel_classicseasonuserteamprivateleague
GROUP BY private_league_id ) AS LC
LEFT JOIN private_league PL ON PL.priv_league_id = LC.pID
Now add in host user name
SELECT PL.league_name, LC.uCNT as total_users, hu.name as host_user
FROM (SELECT private_league_id AS pID, COUNT(*) AS uCNT
FROM classicseasonmodel_classicseasonuserteamprivateleague
GROUP BY private_league_id ) AS LC
LEFT JOIN private_league PL ON PL.priv_league_id = LC.pID
LEFT JOIN user hu ON PL.host_user = hu.user_id
Don't know where current position is.
This query will give users and position for each team, join to this and limit by user id to get one users position for each team:
select UT.user_id,
UT.team_id,
ROW_NUMBER() OVER (PARTITION BY team_id ORDER BY total_score DESC) AS team_position
from private_league L
join user_team_private_league_M2M LJ ON L.priv_league_id = LJ.priv_league_id
join user_team UT ON LJ.user_team_id = UT.user_team_id

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.