Complex SQL Union - mysql

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

Related

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:

Getting percentage of total in SQL with two joins

So I'm trying to do something that I think should be fairly simple with SQL. But I'm having a hard time figuring it out. Here is the format of my data:
One table with user information, let's call it User:
ID name_user Drive_Type
1 Tim Stick shift
2 Jim Automatic
3 Bob Automatic
4 Lisa Stick shift
Then I have one table used for the join, let's call it Join_bridge:
user_ID car_has_ID
1 12
2 13
3 14
4 14
And one table with car information, let's call it Car:
car_ID name
12 Honda
13 Toyota
14 Ford
Then what I want is something that looks like this with the total number of Ford's that are stick shift and the percentage
name Total percentage
Ford 1 25%
I have tried the following, which gets the total right, but not the percentage:
select Drive_Type,
name,
count(Drive_Type) as Total,
(count(Drive_Type) / (select count(*)
from User
join Join_bridge
on User.ID = user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
) * 100.0 as Percent
from User
join Join_bridge
on User.ID = Join_bridge.user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
where name = 'Ford' and Drive_Type = "Automatic"
;
What am I missing? Thanks.
See this SQL Fiddle with the query - the trick is to SUM over CASE that returns 1 for rows you look for and 0 for the rest in order to calculate "Total" at the same time you can also count all rows to calculate percentage.
Here's the SQL query:
SELECT
'Ford' name,
SUM(a.ford_with_stack_flag) Total,
100.0 * SUM(a.ford_with_stack_flag) / COUNT(*) percentage
FROM (
SELECT
Car.name,
(CASE WHEN User.Drive_Type = 'Stick Shift' and Car.name = 'Ford' THEN 1 ELSE 0 END) ford_with_stack_flag
FROM User
JOIN Join_bridge on User.ID = Join_bridge.user_ID
JOIN Car ON Car.car_ID = Join_bridge.car_has_ID
) a
Compute percent and join to Car. Window functions are supported in MySql 8.0
select c.car_ID, c.name, p.cnt, p.Percent
from car c
join (
select car_has_ID, u.Drive_Type,
count(*) cnt,
count(*) / count(count(*)) over() Percent
from Join_bridge b
join user u on u.ID = b.user_ID
group by b.car_has_ID, u.Drive_Type
) p on p.car_has_ID = c.car_ID
where c.name = 'Ford' and p.Drive_Type='Stick shift';
db<>fiddle

Compare two lists and return values not contained in the other list

Employee List (List 1)
USER ID NAME
1 John
2 Jane
3 Rob
4 Bill
5 Sally
Enrolled Students (List 1)
ID PID USER_ID
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
I am trying to find a way to determine if I want to look up who is not enrolled in x course.
So if I wanted to know which employees were not enrolled in Course 1 the result would be
USER_ID
3
4
5
Then if I wanted to know who is not enrolled in course 2
USER_ID
4
5
I tried this however it returns all students enrolled in the course. Where if the student has not been enrolled there is no NULL pid.
SELECT e.user_id, e.full_name, es.student
FROM employees e LEFT OUTER JOIN
enrolled_students es
ON e.user_id = es.student AND es.pid = 40
WHERE e.level = 3 AND es.student IS NULL ;
First we have to check who does enrolled in the course, then we have to get the list of names containing the other names except the ones returned in the first part of the query. Something like this can be done for this purpose:
SELECT
e1.*
FROM
employee e1
LEFT JOIN
(SELECT
e.user_id
FROM
employee e
JOIN enrolled_student es ON e.user_id = es.user_id
WHERE
es.pid = 1) t ON e1.user_id = t.user_id
WHERE
t.user_id IS NULL;
Try this:
select id from users where id not in (select user_id from enrolled where pid = 1)
Selects all users, which are not enrolled to course 1.

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.

Selecting column based on a field value in MYSQL

I need to select a different column from a database based on a value in a different field.
Players:
ID EVENT_ID NAME TEAM
--------------------------------
1 1 Ann 1
2 1 Bob 2
3 2 Claire 1
Events:
ID EVENT_NAME TEAM_1 TEAM_2
----------------------------------------------
1 Football All Stars Tornadoes
2 Tennis Dynamos Best Team
Based on my tables I want to be able to search for player ID 2 and get their team name depending on the players.team value.
so something like this:
SELECT players.*,
(SELECT team+"players.team" AS team_name FROM events WHERE players.event_id = events.id)
WHERE players.id = '2'
that gets the result:
Player.ID: 1
Player.Name: Bob
Team_Name: Tornadoes
You can use CASE for that:
SELECT p.id, p.name,
CASE WHEN p.team = 1 THEN e.team_1 ELSE e.team_2 END AS Team_Name
FROM Players p
LEFT JOIN Events e
ON e.id = p.event_id
WHERE p.id = 2
SELECT p.id,p.name, IF(p.team = 1,e.team_1,e.team_2)
FROM players p
LEFT JOIN events e ON (p.event_id = e.id)
WHERE p.id = 2;