WITH ROLLUP issue on range order - mysql

After adding the WITH ROLLUP in the GROUP BY statement the ranges reordered. How can this be fixed?
Here is the code
SUM(product.product_id = 1) AS Soda,
SUM(product.product_id = 2) AS Liquor,
SUM(product.product_id = 3) AS Lemon,
SUM(product.product_id = 4) AS Mango,
SUM(product.product_id = 5) AS Inhaler,
SUM(1) AS Count
FROM line_item
JOIN product USING (product_id)
JOIN ( SELECT 0 lowest, 500 highest UNION
SELECT 501 , 1000 UNION
SELECT 1001 , 1500 UNION
SELECT 1501 , 2000 UNION
SELECT 2001 , 2500 ) ranges ON product.price * line_item.quantity BETWEEN ranges.lowest AND ranges.highest
GROUP BY Revenue WITH ROLLUP;
Result:
+-------------+------+--------+-------+-------+---------+-------+
| Revenue | Soda | Liquor | Lemon | Mango | Inhaler | Count |
+-------------+------+--------+-------+-------+---------+-------+
| 0 - 500 | 4 | 0 | 4 | 0 | 1 | 9 |
| 1001 - 1500 | 0 | 1 | 0 | 2 | 2 | 5 |
| 1501 - 2000 | 0 | 2 | 0 | 0 | 1 | 3 |
| 2001 - 2500 | 0 | 1 | 0 | 0 | 0 | 1 |
| 501 - 1000 | 0 | 0 | 0 | 2 | 0 | 2 |
| NULL | 4 | 4 | 4 | 4 | 4 | 20 |
+-------------+------+--------+-------+-------+---------+-------+
The range 501 - 1000 moved to the bottom, it should be next to the 0-500 range.

The column Revenue is a string so the results are sorted alphabetically.
In order to sort the column as a number, a solution would be to cast Revenue to a number like:
ORDER BY Revenue IS NULL, Revenue + 0
but as I tested in MySql 8.0.22 here (with a previous fiddle of your data), for some reason, it does not work (maybe a bug?).
In any case you should try it too.
The code that worked is this:
GROUP BY ranges.lowest, ranges.highest WITH ROLLUP
HAVING GROUPING(ranges.lowest) = 1 OR GROUPING(ranges.highest) = 0
ORDER BY GROUPING(ranges.lowest), ranges.lowest
See the demo.
Results:
> Revenue | Soda | Liquor | Lemon | Mango | Inhaler | Count
> :-------- | ---: | -----: | ----: | ----: | ------: | ----:
> 0-500 | 4 | 0 | 4 | 0 | 1 | 9
> 501-1000 | 0 | 0 | 0 | 2 | 0 | 2
> 1001-1500 | 0 | 1 | 0 | 2 | 2 | 5
> 1501-2000 | 0 | 2 | 0 | 0 | 1 | 3
> 2001-2500 | 0 | 1 | 0 | 0 | 0 | 1
> null | 4 | 4 | 4 | 4 | 4 | 20

Related

Creating Medalist MySQL view from a table

I need a help to create a view in MySQL.
I have a table in the name of competitions like below:
+---------+-----+-----+-----+
|id| name |rank1|rank2|rank3|
+--+------+-----+-----+-----+
| 1| cmpt1| 4 | 3 | 9 |
| 2| cmpt2| 3 | 7 | 8 |
| 3| cmpt3| 4 | 1 | 2 |
| 4| cmpt4| 5 | 8 | 4 |
| 5| cmpt5| 9 | 3 | 2 |
| 6| cmpt6| 1 | 8 | 2 |
+--+------+-----+-----+-----+
the rank1,2,3 values refer to the player id who has such rank at the end of that competition.
Now I want to create a MySQL view to show each player's total medals. Rank 1, 2, and 3 received gold, silver, and bronze medal respectively.
The output of the view will be like following table:
+------+------------+-------------+-------------+
|player| gold_medals|silver_medals|bronze_medals|
+------+------------+-------------+-------------+
| 1 | 4 | 7 | 1 |
| 2 | 7 | 0 | 9 |
| 3 | 1 | 4 | 6 |
| 4 | 0 | 2 | 8 |
| 5 | 2 | 8 | 0 |
| 6 | 3 | 1 | 1 |
+------+------------+-------------+-------------+
Thanks in advance
I assumed you have another table for list players :
select p.playerid
, count(case when playerid = rank1 then 1 end) gold_medals
, count(case when playerid = rank2 then 1 end) silver_medals
, count(case when playerid = rank3 then 1 end) bronze_medals
from
players p
left join ranks r
on p.playerid in (rank1, rank2, rank3)
group by p.playerid
playerid | gold_medals | silver_medals | bronze_medals
-------: | ----------: | ------------: | ------------:
1 | 1 | 1 | 0
2 | 0 | 0 | 3
3 | 1 | 2 | 0
4 | 2 | 0 | 1
5 | 1 | 0 | 0
6 | 0 | 0 | 0
7 | 0 | 1 | 0
8 | 0 | 2 | 1
9 | 1 | 0 | 1
db<>fiddle here
You can unpivot and aggregate:
select playerid,
sum(ranking = 1) as num_gold,
sum(ranking = 2) as num_silver,
sum(ranking = 3) as num_bronze
from ((select rank1 as playerid, 1 as ranking
from ranks
) union all
(select rank2, 2 as ranking
from ranks
) union all
(select rank3, 3 as ranking
from ranks
)
) p
group by playerid;
Note: This only includes players who have a ranking. Your question doesn't include a source of all players, so that seems sufficient.
Here is a db<>fiddle.
Note that older versions of MySQL (pre-5.7 I think) don't support subqueries in the FROM clause of views. Happily that restriction is no longer in force.

Need result for the MySQL joins for this method

I have two table one is order_details and second is orders.
1) order_details
id | order_id | item | order_units | is_cancelled |
------------------------------------------------------------------
1 | 00001 | Mobile | 2 | 0 |
2 | 00001 | Headphone | 2 | 0 |
3 | 00001 | cover | 5 | 0 |
4 | 00002 | charger | 1 | 0 |
5 | 00002 | mobile | 1 | 0 |
6 | 00004 | Tablet | 2 | 0 |
7 | 00005 | Mobile | 1 | 0 |
8 | 00005 | Battery | 2 | 1 |
9 | 00006 | Mobile | 1 | 0 |
10 | 00006 | speaker | 1 | 0 |
11 | 00006 | Motinor | 1 | 0 |
12 | 00007 | Laptop | 2 | 0 |
2) orders
order_id | date | time |total_amount| round |discount|refund
-----------------------------------------------------------------------
00001 | 2017-06-16 |10:10:45 | 456.12 |-0.12 | 0 | 0
00002 | 2017-06-16 |10:25:45 | 600.00 | 0.00 | 10 | 50
00004 | 2017-06-16 |11:10:45 | 300.55 |-0.05 | 0 | 0
00005 | 2017-06-16 |12:10:45 | 200.45 | 0.05 | 20 | 0
00006 | 2017-06-16 |12:40:45 | 685.00 | 0.00 | 50 | 0
00007 | 2017-06-24 |14:10:45 | 888.35 | 0.15 | 0 | 0
I want to join the "order_details" with "orders" and the result should be as below:
---------------------------------------------------------------------
date | time | hour |order_count| total_units| net_amount
---------------------------------------------------------------------
| 2017-06-16 |10:10:45 | 10 | 2 | 11 | 996
| 2017-06-16 |11:10:45 | 11 | 1 | 2 | 300.50
| 2017-06-16 |12:40:45 | 12 | 2 | 4 | 180.50
--------------------------------------------------------------------
I have created a sql query for the above result format and all columns outputs are correct except the "total_units", its showing null.
The following query i have used:
SELECT hdr.date,hdr.time, LPAD(HOUR(hdr.time),2,'0') AS hour, COUNT(hdr.`order_id`) AS order_count, dtl.total_units, SUM((hdr.total_amount+hdr.round-hdr.discount)-hdr.refund) AS net_amount
FROM orders hdr
LEFT JOIN (
SELECT order_id, SUM(qty) AS total_units
FROM order_details
WHERE is_cancelled=0) dtl ON dtl.order_id = hdr.order_id
WHERE DATE(hdr.date) = '2017-06-16 ' AND (HOUR(hdr.time) BETWEEN ('10') AND ('12'))
GROUP BY hdr.date, HOUR(hdr.time)
Please help me to correct this query and generate the exact output as above.
Sorry, One correction in my query..
SELECT hdr.date,hdr.time, LPAD(HOUR(hdr.time),2,'0') AS hour, COUNT(hdr.`order_id`) AS order_count, dtl.total_units, SUM((hdr.total_amount+hdr.round-hdr.discount)-hdr.refund) AS net_amount
FROM orders hdr
LEFT JOIN (
SELECT order_id, SUM(order_units) AS total_units
FROM order_details
WHERE is_cancelled=0) dtl ON dtl.order_id = hdr.order_id
WHERE DATE(hdr.date) = '2017-06-16 ' AND (HOUR(hdr.time) BETWEEN ('10') AND ('12'))
GROUP BY hdr.date, HOUR(hdr.time)
I have made some corrections in the query and it is working fine now:
SELECT hdr.date,hdr.time, LPAD(HOUR(hdr.time),2,'0') AS hour, COUNT(hdr.`order_id`) AS order_count, SUM(dtl.order_units) AS total_units, SUM((hdr.total_amount+hdr.round-hdr.discount)-hdr.refund) AS net_amount
FROM orders hdr
LEFT JOIN (
SELECT order_id, SUM(order_units) AS order_units
FROM order_details
WHERE is_cancelled=0 GROUP BY order_id) dtl ON dtl.order_id = hdr.order_id
WHERE DATE(hdr.date) = '2017-06-16 ' AND (HOUR(hdr.time) BETWEEN ('10') AND ('12'))
GROUP BY hdr.date, HOUR(hdr.time)
Thank you.

How to write mysql innerquery when get the records count from same table

Query to get results from 2 tables:
SELECT path.* FROM (SELECT tbc.course_name
slp.course_id,slp.student_type,slp.stu_reference_id,
count(slp.course_id) as counttotalWatchedStudents
from tbl_student_learning_path slp LEFT JOIN tbl_courses tbc
on tbc.course_pid = slp.course_id WHERE slp.stu_reference_id =34
and slp.student_type='institute' GROUP BY slp.course_id ) as path
Query result table:
| course_id | totalCollegeStudents | fullwatchedStudentsCount | counttotalWatchedStudents | sumOfWatchPointsForWatchedStudents | totalStudentsAvg | fullwatchedAvg |
|-------------------------------|----------------------|--------------------------|---------------------------|------------------------------------|------------------|----------------|
| Number Systems | 9 | 0 | 3 | 60 | 20.0000 | 0 |
| Percentages | 9 | 0 | 3 | 30 | 10.0000 | 0 |
| Blood Relations | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Calandar | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Percentages | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Permutation & Combination | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Probability | 9 | 0 | 3 | 90 | 30.0000 | 0 |
| Ratios | 9 | 0 | 3 | 120 | 40.0000 | 0 |
| Time and Work | 9 | 0 | 3 | 150 | 50.0000 | 0 |
| Time Speed & Distance | 9 | 1 | 3 | 140 | 46.6667 | 100 |
| Averages | 9 | 3 | 3 | 300 | 100.0000 | 300 |
| Coding and Decoding | 9 | 3 | 3 | 300 | 100.0000 | 300 |
From the above table, i want to add inner query to the main query
query should be something like this:
( select count(t1.watched_percentage) from tbl_student_learning_path t1
WHERE t1.stu_reference_id =34 and t1.student_type='institute'
AND t1.watched_percentage >= 90 group by t1.course_id )
fullwatchedStudentsCount,
And the result should come like this(this is nothing but if first table counttotalWatchedStudents value is 3 it means that there are 2 type of people
1.students watched full (watched_percentage>=90)
2.students still watching (watched_percentage 1-89)
)
| fullwatchedStudentsCount | fullwatchedStudentsSum |
|--------------------------|------------------------|
| 2 | 200 |
| 1 | 100 |
| 0 | 0 |
| 2 | 200 |
| 1 | 100 |
| 1 | 100 |
| 0 | 0 |
| 2 | 200 |
| 2 | 200 |
| 1 | 100 |
| 2 | 200 |
| 1 | 100 |
If I understand correctly you want sum up the students which saw more than 90% in another column:
I'd do it like this:
SELECT tbc.course_name,
slp.course_id,
slp.student_type,
slp.stu_reference_id,
count(slp.course_id) AS counttotalWatchedStudents,
sum(if(slp.watched_percentage>=90, 1, 0)) AS fullwatchedStudentsCount
sum(if(slp.watched_percentage>=90, watched_percentage, 0)) AS fullwatchedStudentsSum
FROM tbl_student_learning_path slp
LEFT JOIN tbl_courses tbc ON tbc.course_pid = slp.course_id
WHERE slp.stu_reference_id =34
AND slp.student_type='institute'
GROUP BY slp.course_id
enter code here
Hope this helps
So you want to count students with a watched_percentage >= 90? Use conditional aggregation for this:
SELECT
tbc.course_name,
slp.course_id,
slp.student_type,
slp.stu_reference_id,
COUNT(*) as counttotalWatchedStudents,
SUM(slp.watched_percentage >= 90) as fullwatchedStudentsCount,
SUM(slp.watched_percentage < 90) as stillwatchedStudentsCount
FROM tbl_student_learning_path slp
LEFT JOIN tbl_courses tbc ON tbc.course_pid = slp.course_id
WHERE slp.stu_reference_id = 34
AND slp.student_type= 'institute'
GROUP BY slp.course_id;
MySQL treats true = 1 and false = 0, so you can simply sum the trues :-)

MySql query count and distinct

I have a table 'questions' with columns
userid
qid(question id)
answer
The questions are multiple choice so not every question has the same number of answers a user can choose from.
question 100 might have 4 answers to choose from.
question 200 might have 6 answers to choose from.
question 300 might have 2 answers to choose from.
etc
So the table might look something like this:
+-------- --+---------+--------+
| userid | qid | answer |
+---- ------+---------+--------+
| 1 | 100 | 4 |
| 1 | 200 | 6 |
| 1 | 300 | 1 |
| 1 | 400 | 4 |
| 2 | 100 | 1 |
| 2 | 400 | 6 |
| 3 | 200 | 4 |
| 3 | 400 | 4 |
| 3 | 100 | 1 |
| 4 | 100 | 1 |
| 4 | 400 | 6 |
| 5 | 200 | 1 |
| 5 | 400 | 6 |
+-----------+---------+--------+
I want to know what's the count for the most given answer for question 100, what's the count for the second highest answer for question 100 , what's the count for the 3rd highest answer for question 100, etc
I've can't figure how to query this and not sure if this is possible.
I would the results to be something like:
+------+----+----+----+----+----+----+----+
| qid |ans1|ans2|ans3|ans4|ans5|ans6|ans7|
+------+----+----+----+----+----+----+----+
| 100 | 3 | 0 | 0 | 1 | 0 | 0 | 0 |
| 200 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 300 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 400 | 0 | 2 | 0 | 0 | 0 | 3 | 0 |
+------+----+----+----+----+----+----+----+
Group by the questions and then you can use aggregate functions like sum() that apply to each group. And you can use a condition in sum() to do conditional summing
select qid,
sum(answer = 1) as ans1,
sum(answer = 2) as ans2,
sum(answer = 3) as ans3,
sum(answer = 4) as ans4,
sum(answer = 5) as ans5,
sum(answer = 6) as ans6,
sum(answer = 7) as ans7,
count(*) as total
from your_table
group by qid

How can I order a table from another table's column then run a query?

I'm building a website for our ball team for the fun of it and keeping track of stats using PHP and SQL for the database. I've learned both by reading the manuals and through forums. I'm working on building a query that will display the current longest hitting streak. I stumbled across a page about detecting runs and streaks and am trying to work with that. I'm really new to all this stuff, so maybe I've structured my tables incorrectly.
Table "games"
+--------+------------+------+
| GameID | Date | Time |
+--------+------------+------+
| 1 | 2015/08/19 | 6:30 |
| 2 | 2015/08/20 | 6:30 |
| 3 | 2015/08/22 | 6:30 |
| 4 | 2015/08/24 | 8:00 |
| 5 | 2015/08/24 | 6:30 |
| 6 | 2015/07/15 | 8:00 |
+--------+------------+------+
Table "player"
+--------+----+---+
| GameID | AB | H |
+--------+----+---+
| 1 | 3 | 1 |
| 2 | 4 | 2 |
| 3 | 2 | 0 |
| 4 | 3 | 0 |
| 5 | 2 | 1 |
| 6 | 3 | 0 |
+--------+----+---+
Code
SELECT games.GameID, GR.H,
(SELECT COUNT(*)
FROM player G
WHERE (CASE WHEN G.H > 0 THEN 1 ELSE 0 END) <> (CASE WHEN GR.H > 0 THEN 1 ELSE 0 END)
AND G.GameID <= GR.GameID) as RunGroup
FROM player GR
INNER JOIN games
ON GR.gameID = games.GameID
ORDER BY Date ASC, Time ASC
Basically in order to correctly get the hit streak right, I need to reorder the GameIDs on the "player" table based on the Date (ASC) and Time (ASC) on the "games" table before executing the RunGroup part of the code. Obviously by adding the ORDER BY, everything gets sorted only after the RunGroup has finished querying and results in incorrect data. I've been stuck here for a few days and now need some help.
The Result I currently get is:
+--------+---+----------+
| GameID | H | RunGroup |
+--------+---+----------+
| 6 | 0 | 3 |
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 0 | 2 |
| 5 | 1 | 2 |
| 4 | 0 | 2 |
+--------+---+----------+
This is what I'm trying to achieve:
+--------+---+----------+
| GameID | H | RunGroup |
+--------+---+----------+
| 6 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 0 | 2 |
| 5 | 1 | 2 |
| 4 | 0 | 3 |
+--------+---+----------+
Thanks
Consider the following:
DROP TABLE IF EXISTS games;
CREATE TABLE games
(game_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,date_played DATETIME NOT NULL
);
INSERT INTO games VALUES
(1,'2015/08/19 18:30:00'),
(2,'2015/08/20 18:30:00'),
(3,'2015/08/22 18:30:00'),
(4,'2015/08/24 20:00:00'),
(5,'2015/08/24 18:30:00'),
(6,'2015/07/15 20:00:00');
DROP TABLE IF EXISTS stats;
CREATE TABLE stats
(player_id INT NOT NULL
,game_id INT NOT NULL
,at_bat INT NOT NULL
,hits INT NOT NULL
,PRIMARY KEY(player_id,game_id)
);
INSERT INTO stats VALUES
(1,1,3,1),
(1,2,4,2),
(1,3,2,0),
(1,4,3,0),
(1,5,2,1),
(1,6,3,0),
(2,1,2,1),
(2,2,3,2),
(2,3,3,0),
(2,4,3,1),
(2,5,2,1),
(2,6,3,0);
SELECT x.*
, SUM(y.at_bat) runningAB
, SUM(y.hits) runningH
, SUM(y.hits)/SUM(y.at_bat) BA
FROM
(
SELECT s.*, g.date_played FROM stats s JOIN games g ON g.game_id = s.game_id
) x
JOIN
(
SELECT s.*, g.date_played FROM stats s JOIN games g ON g.game_id = s.game_id
) y
ON y.player_id = x.player_id
AND y.date_played <= x.date_played
GROUP
BY x.player_id
, x.date_played;
+-----------+---------+--------+------+---------------------+-----------+----------+--------+
| player_id | game_id | at_bat | hits | date_played | runningAB | runningH | BA |
+-----------+---------+--------+------+---------------------+-----------+----------+--------+
| 1 | 6 | 3 | 0 | 2015-07-15 20:00:00 | 3 | 0 | 0.0000 |
| 1 | 1 | 3 | 1 | 2015-08-19 18:30:00 | 6 | 1 | 0.1667 |
| 1 | 2 | 4 | 2 | 2015-08-20 18:30:00 | 10 | 3 | 0.3000 |
| 1 | 3 | 2 | 0 | 2015-08-22 18:30:00 | 12 | 3 | 0.2500 |
| 1 | 5 | 2 | 1 | 2015-08-24 18:30:00 | 14 | 4 | 0.2857 |
| 1 | 4 | 3 | 0 | 2015-08-24 20:00:00 | 17 | 4 | 0.2353 |
| 2 | 6 | 3 | 0 | 2015-07-15 20:00:00 | 3 | 0 | 0.0000 |
| 2 | 1 | 2 | 1 | 2015-08-19 18:30:00 | 5 | 1 | 0.2000 |
| 2 | 2 | 3 | 2 | 2015-08-20 18:30:00 | 8 | 3 | 0.3750 |
| 2 | 3 | 3 | 0 | 2015-08-22 18:30:00 | 11 | 3 | 0.2727 |
| 2 | 5 | 2 | 1 | 2015-08-24 18:30:00 | 13 | 4 | 0.3077 |
| 2 | 4 | 3 | 1 | 2015-08-24 20:00:00 | 16 | 5 | 0.3125 |
+-----------+---------+--------+------+---------------------+-----------+----------+--------+
I rebuilt my database to have only one table to contain the stats from all players. From there i was able to use this query to find my longest current hitting streak for a certain player.
SELECT *
FROM (SELECT (CASE WHEN h > 0 THEN 1 ELSE 0 END) As H, MIN(date_played) as StartDate,
MAX(date_played) as EndDate, COUNT(*) as Games
FROM (SELECT date_played, (CASE WHEN h > 0 THEN 1 ELSE 0 END) as H, (SELECT COUNT(*)
FROM stats G WHERE ((CASE WHEN G.h > 0 THEN 1 ELSE 0 END) <> (CASE WHEN GR.h > 0 THEN 1 ELSE 0 END))
AND G.date_played <= GR.date_played AND player_id = 13) as RunGroup
FROM stats GR
WHERE player_id = 13) A
GROUP BY H, RunGroup
ORDER BY Min(date_played)) A
WHERE H = 1
ORDER BY Games DESC
LIMIT 1