How to optimize this in MySQL? - mysql

I have table structure as displayed in first table.
And want to fetch Both Male and Female Counts in a single query so that request will go only for one time onto the server.

This is what you need to do:
select gender,
count(case when age between 0 and 20 then 1 else null end) Age_0_20,
count(case when age between 21 and 40 then 1 else null end) Age_21_40
from yourtable
group by gender
Adjust accordingly :)
Update, with clarifications
Note that COUNT aggregate function only counts non-null values. Thus, the else values in the case must be NULL. The When value returns 1 but it could just be any non-null value.
Some people implement this by using SUM:
select gender,
sum(case when age between 0 and 20 then 1 else 0 end) Age_0_20,
sum(case when age between 21 and 40 then 1 else 0 end) Age_21_40
from yourtable
group by gender
The result is going to be absolutely the same.

Related

How can I query the results of a MySQL query and get a COUNT() of the results that fall within a certain range?

thanks for taking the time.
I have the following query:
SELECT bike_id,
COUNT(trip_id) AS "trip_count",
COUNT(trip_id)/365 AS "avg_rides_per_day"
FROM 2017
GROUP BY bike_id;
Which gives me the results I want which are the distinct 'bike_id'(as there are no duplicates), the count of all the rides each 'bike_id' made in 2017, and the quick average for the year for each 'bike_id'.
From here, id like to know if I can have it display the COUNT() of the "avg_rides_per_day" in different ranges, from this same result of the query above(since this info doesn't exist in the table without using this query). For example:
0.0000-0.5000 has 328
0.5001-1.0000 has 211
1.0001-1.5000 has 101
(but in a table format, the ranges being the headers)
Hope this makes some sense. I feel its a quick problem to solve but I'm not good(total newbie).
Thank you!
So i figured it out so hopefully if any new people run across this issue this can help you a little bit!:
SELECT COUNT(CASE WHEN avy BETWEEN 0.0000 AND 0.5000 THEN 1 ELSE NULL END) AS "0.0000-0.5000",
COUNT(CASE WHEN avy BETWEEN 0.5001 AND 1.0000 THEN 1 ELSE NULL END) AS "0.5001-1.0000",
COUNT(CASE WHEN avy BETWEEN 1.0001 AND 1.5000 THEN 1 ELSE NULL END) AS "1.0001-1.5000",
COUNT(CASE WHEN avy BETWEEN 1.5001 AND 2.0000 THEN 1 ELSE NULL END ) AS "1.5001-2.0000",
COUNT(CASE WHEN avy BETWEEN 2.0001 AND 2.5000 THEN 1 ELSE NULL END) AS "2.0001-2.5000",
COUNT(CASE WHEN avy BETWEEN 2.5001 AND 3.0000 THEN 1 ELSE NULL END ) AS "2.5001-3.0000",
COUNT(CASE WHEN avy BETWEEN 3.0001 AND 3.5000 THEN 1 ELSE NULL END ) AS "3.0001-3.5000"
FROM (SELECT `bike_id`,
1 * COUNT(`trip_id`)/365 AS avy
FROM `2021`
GROUP BY `bike_id`) AS averages;
The result is everything neatly in its own column, indicating range(as the column header) and the count of unique bikes that fall in that range(as the only entry in each column).

Count is displaying one only row despite multiple rows in database

Here is my SQL query:
SELECT
COUNT(CASE WHEN `urgency`='1' THEN 1 END) AS verylow,
COUNT(CASE WHEN `urgency`='2' THEN 1 END) AS low,
COUNT(CASE WHEN `urgency`='3' THEN 1 END) AS standard,
COUNT(CASE WHEN `urgency`='4' THEN 1 END) AS high,
COUNT(CASE WHEN `urgency`='5' THEN 1 END) AS critical,
tbl_users.userName
FROM
notes, tbl_users
WHERE
notes.responsible = tbl_users.userID
AND project_id = '4413'
AND (notes.status = 'Ongoing' OR notes.status = 'Not started')
and the output is:
verylow low standard high critical userName
5 1 2 1 1 Nick
However this is wrong because i have multiple users in the database who have assigned tasks. and it looks like this in my database:
urgency userName
3 Nick
5 Nick
4 Nick
3 James
1 James
1 Nick
2 Nick
1 James
1 Nick
1 Nick
Any idea why it doesn't count the urgency for the other user and how many different urgencies he has?
What you are doing is not entirely correct. If you would turn on MySQL mode ONLY_FULL_GROUP_BY, you'd get a warning, because you are selecting a column that is not in the GROUP BY clause without applying an aggregation function. So you need a GROUP BY clause.
The entire query should look like so:
SELECT
COUNT(CASE WHEN `urgency`='1' THEN 1 END) AS verylow,
COUNT(CASE WHEN `urgency`='2' THEN 1 END) AS low,
COUNT(CASE WHEN `urgency`='3' THEN 1 END) AS standard,
COUNT(CASE WHEN `urgency`='4' THEN 1 END) AS high,
COUNT(CASE WHEN `urgency`='5' THEN 1 END) AS critical,
tbl_users.userName
FROM
notes, tbl_users
WHERE
notes.responsible = tbl_users.userID
AND project_id = '4413'
AND (notes.status = 'Ongoing' OR notes.status = 'Not started')
GROUP BY tbl_users.userName;
With COUNT you aggregate your rows. As there is no GROUP BY clause, you aggregate them to one row (rather than, say a row per user).
You are selecting userName. Which? As you select only one row, the DBMS picks one arbitrarily.

SQL division returns NULL value instead of Numeric

I am looking to calculate daily revenue from the data-set below. When there is a 0 that means the asset was not producing revenue and should not be included into the daily revenue calculation. Instead of retrieving a numeric daily revenue, I am returned with a NULL value. The following is my code and the data-set.
**asset_revenue**
15
15
213
0
32
89
-47
SUM([asset_revenue]) / SUM(CASE WHEN asset_revenue <> 0 THEN 7 ELSE NULL END)
In the example above, I am expecting the daily revenue to be 7.54. Is there a reason why SQL is returning a NULL value?
Reason:
SELECT 1 + NULL;
+----------+
| 1 + NULL |
+----------+
| NULL |
+----------+
1 row in set (0.00 sec)
It looks like you are trying to get an average.
Just use 0 instead of NULL. This will result in your denominator equaling the number of rows that have a non-zero asset_revenue.
If the rows represent daily revenue:
SELECT SUM([asset_revenue]) /
CASE
WHEN SUM([asset_revenue]) <> 0 THEN SUM(CASE WHEN asset_revenue <> 0 THEN 1 ELSE 0 END)
ELSE 1 -- Special case where asset_revenue is zero across the entire time period. 0/1 is still zero, so that represents an average revenue of zero.
END
If the rows represent weekly revenue or if the 7 needs to be retained for some other reason, do this:
SELECT SUM([asset_revenue]) /
CASE
WHEN SUM([asset_revenue]) <> 0 THEN SUM(CASE WHEN asset_revenue <> 0 THEN 7 ELSE 0 END)
ELSE 1 -- Special case where asset_revenue is zero across the entire time period. 0/1 is still zero, so that represents an average revenue of zero.
END
#Strawberry is correct as to the reason that your script returns NULL. NULL means you don't know what the value is. If you don't know what a value is, you can't know what happens when that unknown value is put into an arithmetic operation and the result is requested. What is 55 plus some unknown value? I don't know!
In some situations the COALESCE function is handy:
mysql> SELECT COALESCE(123, 1), COALESCE(NULL, 1);
+------------------+-------------------+
| COALESCE(123, 1) | COALESCE(NULL, 1) |
+------------------+-------------------+
| 123 | 1 |
+------------------+-------------------+
1 row in set (0.00 sec)
More specifically:
SUM(asset_revenue) /
COALESCE(SUM(CASE WHEN asset_revenue <> 0 THEN 7 ELSE NULL END), 0)
or maybe
COALESCE( SUM(asset_revenue) /
SUM(CASE WHEN asset_revenue <> 0 THEN 7 ELSE NULL END)
1)
For brevity:
SUM(CASE WHEN asset_revenue <> 0 THEN 7 ELSE NULL END)
-->
7 * SUM(asset_revenue <> 0)
(Except that it can yield 0 instead of NULL)

Obtaining 2 Different results from same column different WHERE in a single query

SELECT COUNT(NAME) AS NAMEA
FROM (DATA.1 WHERE MARKS > 50), COUNT(NAME) AS NAMEB FROM DATA.1
After running it I get this
Syntax error in JOIN operation
I am trying to get the percentage of student pass, no. of student pass, total no. of student and no. of student fail.
Please help me to find whats wrong in the above Query.
please help me.
Thanks
You're essentially running two queries in one.
Query 1:
SELECT COUNT(NAME) AS NAMEA
FROM DATA1
WHERE MARKS > 50
Query 2:
SELECT COUNT(NAME) AS NAMEB
FROM DATA1
If you want both columns in the same query then you would have to use a SUM of a CASE WHEN for the first query.
SELECT SUM(CASE WHEN MARKS > 50 THEN 1 ELSE 0 END) AS NAMEA,
COUNT(NAME) AS NAMEB
FROM DATA1
For the rest of your points you would need the query:
SELECT COUNT(NAME) AS TOTAL_STUDENTS,
SUM(CASE WHEN MARKS > 50 THEN 1 ELSE 0 END) AS STUDENTS_PASSED,
SUM(CASE WHEN MARKS > 50 THEN 1 ELSE 0 END)/COUNT(NAME) AS PASS_RATE,
SUM(CASE WHEN MARKS < 50 THEN 1 ELSE 0 END) AS STUDENTS_FAILED
FROM DATA1
Bear in mind that this missed out students that have exactly 50 marks. If 50 is a pass then you would need to use >= 50 for STUDENTS_PASSED. If 50 is a fail then you would need to use <= 50 for STUDENTS_FAILED.

SQL query to group records under separate fields

I'm working with MySQL, and I have the following schema:
id school round score win loss tie
2 My School Name 1 10 1 0 0
3 My School Name 2 20 0 1 0
4 My School Name 3 30 1 0 0
5 My School Name 4 40 1 0 0
6 My School Name 5 50 1 0 0
7 My School Name 6 60 0 0 1
And I need the following output, grouped by school name
School Round1 Round2 Round3 Round4 Round5 Round6 wins losses ties
My School Name 10 20 30 40 50 60 4 1 1
So far I feel like I can use GROUP BY School and SUM(win) as wins to get most of the functionality out of it. The hard part, though, is to get those Round_ fields.
Does anyone know how to do this? Thanks in advance, any help would be much appreciated!
Edit: to clarify, I know I have 10 rounds exactly.
We can use a SELECT statement with a GROUP BY school to create a record for each school. The ties, wins, and losses columns are readily calculated with the SUM aggregate function, as you noted. To target a specific round, we can use some clever math (to avoid verbose conditional statements like the one CodeByMoonlight suggested):
If we want to target round R, we note that "round-R" is 0 only when round == R, otherwise it isn't 0. When we take the NOT of "round-R", 0 gets inverted to 1, while everything else gets set to 0. Now, if we multiply !(round-R) by the score of that round, it will give us 0 when the round is not R (as 0*score = 0) and it will give us "score" when the round is R (as 1*score = score). Next, when we take the SUM of this value over the columns, we add score when round=R and 0 otherwise, effectively giving us just the round R score.
Putting that all together gives:
SELECT school AS `School`,
SUM(!(round-1)*score) AS `Round1`,
SUM(!(round-2)*score) AS `Round2`,
SUM(!(round-3)*score) AS `Round3`,
SUM(!(round-4)*score) AS `Round4`,
SUM(!(round-5)*score) AS `Round5`,
SUM(!(round-6)*score) AS `Round6`,
SUM(!(round-7)*score) AS `Round7`,
SUM(!(round-8)*score) AS `Round8`,
SUM(!(round-9)*score) AS `Round9`,
SUM(!(round-10)*score) AS `Round10`,
SUM(win) AS `wins`,
SUM(loss) AS `losses`,
SUM(tie) AS `ties`
FROM `RoundScores` GROUP BY `school`
where RoundScores is the table in question.
EDIT:
If we do not want to manually add 10, we can use prepared statements :
# Store all the conditionals in a string:
# I was not able to to have round loop from 1 to 10, so I iterated over
# all distinct values of 'round' present in the table.
SET #s = "";
SELECT `round`, (#s := CONCAT( #s , "SUM(!(round-",round, ")*score) AS `Round",round, "`," )) FROM `RoundScores` GROUP BY `round`;
# Combine the conditionals from before with the rest of the statement needed.
SET #qry = CONCAT("SELECT school AS `School`,",#s,"SUM(win) AS `wins`,SUM(loss) AS `losses` FROM `RoundScores` GROUP BY `school`");
# Prepare and execute the statement.
PREPARE stmt1 FROM #qry;
EXECUTE stmt1;
TRY WITH UNION ( not tested)
SELECT SUM(win) AS wins, SUM(loss) AS losses, SUM(tie) AS ties
FROM table
GROUP BY (school)
UNION
SELECT score AS round1 FROM table WHERE round=1
UNION
SELECT score AS round2 FROM table WHERE round=2
.... AND so on..
SELECT School, Sum(Case
When Round = 1 Then Score
Else 0
End) AS Round1, Sum(Case
When Round = 2 Then Score
Else 0
End) AS Round2, Sum(Case
When Round = 3 Then Score
Else 0
End) AS Round3, Sum(Case
When Round = 4 Then Score
Else 0
End) AS Round4, Sum(Case
When Round = 5 Then Score
Else 0
End) AS Round5, Sum(Case
When Round = 6 Then Score
Else 0
End) AS Round6, Sum(Case
When Round = 7 Then Score
Else 0
End) AS Round7, Sum(Case
When Round = 8 Then Score
Else 0
End) AS Round8, Sum(Case
When Round = 9 Then Score
Else 0
End) AS Round9, Sum(Case
When Round = 10 Then Score
Else 0
End) AS Round10, Sum(Wins) AS Wins, Sum(Losses) AS Losses, Sum(Ties) AS Ties
FROM MyTable
GROUP BY School
Should work :)