I'm writing a query for age range, in which I want to show the count of people of all age ranges eg
AGE PEOPLE
"0-10" 0
"11-20" 2
"21-30" 5
"31-40" 0
"41-50" 1
I've tried using
SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS [Under 10],
SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS [11-20],
SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) AS [21-30]
FROM people
But it shows ranges as column names
0-10 11-20 21-30 31-40 41-50
0 2 5 0 1
which i dont want.
I have also tried GROUP BY but it didn't show the ranges in which the count was 0.
You can use UNION ALL:
SELECT '[Under 10]' as Age, SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) as People
FROM people
UNION ALL
SELECT '[11-20]', SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END)
FROM people
UNION ALL
SELECT '[21-30]', SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END)
FROM people;
you case when should be like below
CASE WHEN age < 10 then '0-10'
when age age BETWEEN 11 AND 20 then '11-20'
when age BETWEEN 21 AND 30 then '21-30'
..... end as agegroup,--put here more according to your need
count(*)
from table group by agegroup
You need to perform UNION All for this.
SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS PEOPLE, 'UNDER 10' AS AGE FROM people
UNION ALL
SELECT SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS PEOPLE, `11-20` FROM people
UNION ALL
SELECT SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) , `21-30` FROM people
You want to get the group of result in rows so need to perform UNION in this case.
Please find this link for more info on UNION in MYSQL.link
If you are going to use UNION, use UNION ALL and move the conditions to the WHERE clause:
SELECT '[Under 10]' as Age, COUNT(*)
FROM people
WHERE age < 10
UNION
SELECT '[11-20]', COUNT(*)
FROM people
WHERE BETWEEN 11 AND 20
UNION ALL
SELECT '[21-30]', COUNT(*)
FROM people
WHERE age BETWEEN 21 AND 30;
Filtering and UNION ALL both improve performance. (UNION incurs overhead for removing duplicates).
There are other approaches. For instance, you can unpivot your table:
SELECT grp.age,
(CASE grp
WHEN 1 THEN [Under 10]
WHEN 2 THEN [11-20]
WHEN 3 THEN [21-30]
END)
FROM (SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS [Under 10]
SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS [11-20],
SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) AS [21-30]
FROM people p
) p CROSS JOIN
(SELECT 1 as grp, '[Under 10]' as age UNION ALL
SELECT 2 as grp, '[11-20]' as age UNION ALL
SELECT 3, as grp, '[21-30]' as age
) grps;
Although this looks more complicated, it is much better from a performance perspective, because it only scans the original table once.
There are other variants as well that only touch the original table once.
Related
Below is my SQL query. I am not able to run it on db version below version 8. It is perfectly running on version 8. But my live server is behind version 8. Help me in this please.
WITH ages AS
(
SELECT
ROUND(DATEDIFF(Cast(CURRENT_TIMESTAMP() as Date), Cast(dob as Date)) / 365, 0) as age
FROM artisan_bio
)
SELECT
count(case when age between 0 and 24 then 1 end) as age_00_24_cnt,
count(case when age between 25 and 34 then 1 end) as age_25_34_cnt,
count(case when age between 35 and 44 then 1 end) as age_35_44_cnt,
count(case when age between 45 and 54 then 1 end) as age_45_54_cnt,
count(case when age >= 55 then 1 end) as age_55_xx_cnt
FROM ages
This query should work:
SELECT
count(case when age between 0 and 24 then 1 end) as age_00_24_cnt,
count(case when age between 25 and 34 then 1 end) as age_25_34_cnt,
count(case when age between 35 and 44 then 1 end) as age_35_44_cnt,
count(case when age between 45 and 54 then 1 end) as age_45_54_cnt,
count(case when age >= 55 then 1 end) as age_55_xx_cnt
FROM (
SELECT
ROUND(DATEDIFF(Cast(CURRENT_TIMESTAMP() as Date), Cast(dob as Date)) / 365, 0) as age
FROM artisan_bio
) AS ages;
The change moves your first query to be a derived table with the second query.
I have 70+ years of hourly visibility data that I have already calculated into a monthly average. I now need to produce seasonal averages of DJF, MAM, JJA, SON (seasonal average meaning DEC, JAN, FEB as one average and MAR, APR, MAY, the next and so on) for each of those years. Does anyone have any suggestions as to how to go about this using MySQL Workbench?
I essentially need 4 separate columns of the above mentioned months visibility. Is there something I can do under the GROUP BY function like grouping by a set or anything else?
This is what I have used to get the monthly averages but I'm not sure how to
SELECT date, avg(lowest_visibility) FROM b16.hourly
GROUP BY year(date), month(date)
ORDER BY year(date), month(date) asc
Thanks in advance for any help!
Update: I have completed this task using pivot tables and tediously changing the cells to make sure I have the correct December in my DJF seasonal average, but I would still like any guidance how to do this again in the future.
May not be any better than the query you have (who knows?)
In query union to ignore DEC in it's year and union to a query which pushes the DEC year forward
DROP TABLE IF EXISTS T;
create table t
(dt date, val int);
insert into t values
('2019-12-01',10),
('2020-01-01',25),
('2020-06-01',10),
('2020-10-01',10),
('2020-12-01',10);
select yyyy,
sum(djfsum) / sum(djfcnt) dfj ,
sum(mamcnt) / sum(mamcnt) mam ,
sum(jjasum) / sum(jjacnt) jja ,
sum(sonsum) / sum(soncnt) son
from
(
select year(t.dt) yyyy,
sum(case when MONTH(dt) IN(1,2) then val else 0 end) djfsum,
sum(case when MONTH(dt) IN(1,2) then 1 else 0 end) djfcnt,
sum(case when MONTH(dt) IN(3,4,5) then val else 0 end) mamsum,
sum(case when MONTH(dt) IN(3,4,5) then 1 else 0 end) mamcnt,
sum(case when MONTH(dt) IN(6,7,8) then val else 0 end) jjasum,
sum(case when MONTH(dt) IN(6,7,8) then 1 else 0 end) jjacnt,
sum(case when MONTH(dt) IN(9,10,11) then val else 0 end) sonsum,
sum(case when MONTH(dt) IN(9,10,11) then 1 else 0 end) soncnt
from t
where month(dt) <> 12
group by yyyy
union
select year(dt) + 1,
sum(val) as djfsum,
sum(1) as djfcnt,
0,
0,
0,
0,
0,
0
from t
where month(dt) = 12
group by year(dt) + 1
) a
group by yyyy
;
+------+---------+------+---------+---------+
| yyyy | dfj | mam | jja | son |
+------+---------+------+---------+---------+
| 2020 | 17.5000 | NULL | 10.0000 | 10.0000 |
| 2021 | 10.0000 | NULL | NULL | NULL |
+------+---------+------+---------+---------+
2 rows in set, 4 warnings (0.002 sec)
I've this query
SELECT
trage,
CASE trage
WHEN '<18' THEN SUM(CASE WHEN AGE <18 THEN 1 ELSE 0 END)
WHEN '18-24' THEN SUM(CASE WHEN AGE >= 18 AND AGE <= 24 THEN 1 ELSE 0 END)
WHEN '25-34' THEN SUM(CASE WHEN AGE >= 25 AND AGE <= 34 THEN 1 ELSE 0 END)
WHEN '35-44' THEN SUM(CASE WHEN AGE >= 35 AND AGE <= 44 THEN 1 ELSE 0 END)
WHEN '45-54' THEN SUM(CASE WHEN AGE >= 45 AND AGE <= 54 THEN 1 ELSE 0 END)
WHEN '>=55' THEN SUM(CASE WHEN AGE >= 55 THEN 1 ELSE 0 END)
END Total
FROM
( SELECT
t_personne.pers_date_naissance,
t_personne.pers_date_inscription,
TIMESTAMPDIFF(Year, t_personne.pers_date_naissance, t_personne.pers_date_inscription)
- CASE
WHEN MONTH(t_personne.pers_date_naissance) > MONTH(t_personne.pers_date_inscription)
OR (MONTH(t_personne.pers_date_naissance) = MONTH(t_personne.pers_date_inscription)
AND DAY(t_personne.pers_date_naissance) > DAY(t_personne.pers_date_inscription))
THEN 1 ELSE 0
END AS AGE
FROM t_personne
) AS Total
CROSS JOIN
( SELECT '<18' trage UNION ALL
SELECT '18-24' UNION ALL
SELECT '25-34' UNION ALL
SELECT '35-44' UNION ALL
SELECT '45-54' UNION ALL
SELECT '>=55'
)a
GROUP BY trage
ORDER BY FIELD(trage, '<18', '18-24', '25-34', '35-44', '45-54', '>=55')
it give a table with two columns trage and Total for all categories
How to add a column percentage with a line TOTAL for the column Total and %
Thanks for your help
For the time being, you can't do this. To support this MySQL needs Window Function support which it still doesn't have. If you need functions like these I would recommend switching to PostgreSQL.
Also take a look at this question: MySql using correct syntax for the over clause
I have a query that looks like this:
select
price,
item_id,
sum(price),
count(item_id)
from transactions
group by
(price <= 20),
(price between 21 and 30),
(price between 31 and 40),
(price between 41 and 50),
(price > 50)
I have never done a group like this before when I wrote it I was just guessing to see if the query was even valid, and it was. But my question is, is it really getting me what I want?
I want all transactions grouped by:
Items that cost less than or equal to $20
Items that cost between $21 and $30
Items that cost between $31 and $40
Items that cost between $41 and $50
Items that cost more than $50
So, is that query doing what I am asking?
The way to do this in standard SQL (and MySQL) is to use the case statement. Also, I put the definition in a subquery like this:
select pricegrp, sum(price), count(item_id)
from (select t.*,
(case when price <= 20 then '00-20'
when price between 21 and 30 then '21-30'
when price between 31 and 40 then '31-40'
when price between 41 and 50 then '41-50'
when price > 50 then '50+'
end) as pricegrp
from transactions t
) t
group by pricegrp
Also, do you want to group by item_id as well? Or are you just trying to return one arbitrary item? Based on what you want, I'm removing the item_id from the select clause. It doesn't seem necessary.
Your query actually does work in MySQL, in the sense that it runs. It is going to produce one row for each group that you want, so in that sense it "works". However, within each group, it is going to choose an arbitrary price and item_id. These are not explicitly mentioned in the group by clause, so you are using a MySQL (mis)feature called Hidden Columns. Different runs of the query or slight changes to the data or slight changes to the query can change the values of price and item_id returned for each group.
I strongly suggest that you actually name the group. This makes the query and the output much clearer.
Also, I recommend that you get in the habit of putting all columns in the select in the group by clause. There are a few cases where hidden columns are actually useful, but I think, in general, you should depend on them sparingly.
If the price is not stored as an integer, then correct logic is:
select pricegrp, sum(price), count(item_id)
from (select t.*,
(case when price <= 20 then '00-20'
when price <= 30 then '21-30'
when price <= 40 then '31-40'
when price <= 50 then '41-50'
when price > 50 then '50+'
end) as pricegrp
from transactions t
) t
group by pricegrp
SELECT
price,
item_id,
sum(price),
count(item_id),
IF(price<=20,0,IF(price<=30,1,IF(price<=40,2,IF(price<=50,3,4)))) AS pricegroup
FROM transactions
GROUP BY pricegroup
or even
SELECT
price,
item_id,
sum(price),
count(item_id)
FROM transactions
GROUP BY
IF(price<=20,0,IF(price<=30,1,IF(price<=40,2,IF(price<=50,3,4))))
SELECT price,
item_id,
SUM(CASE WHEN price <= 20 THEN price ELSE 0 END) `(price <= 20) SUM`,
SUM(CASE WHEN price <= 20 THEN 1 ELSE 0 END) `(price <= 20) COUNT`,
SUM(CASE WHEN price between 21 and 30 THEN price ELSE 0 END) `(price <= 20) SUM`,
SUM(CASE WHEN price between 21 and 30 THEN 1 ELSE 0 END) `(price <= 20) COUNT`,
SUM(CASE WHEN price between 31 and 40 THEN price ELSE 0 END) `price between 31 and 40 SUM`,
SUM(CASE WHEN price between 31 and 40 THEN 1 ELSE 0 END) `price between 31 and 40 COUNT`,
SUM(CASE WHEN price between 41 and 50 THEN price ELSE 0 END) `price between 41 and 50 SUM`,
SUM(CASE WHEN price between 41 and 50 THEN 1 ELSE 0 END) `price between 41 and 50 COUNT`,
SUM(CASE WHEN price > 50 THEN price ELSE 0 END) `price > 50 SUM`,
SUM(CASE WHEN price > 50 THEN 1 ELSE 0 END) `price > 50 COUNT`
FROM transactions
GROUP BY price, item_id
I have a table with a column that lists ages of users. I want to bin ages in arbitrary groupings (13-17,18-25, etc) and then be able to group by those bins and count users in each group. How can I accomplish this in a query?
SELECT CASE WHEN age BETWEEN 13 AND 17 THEN '13-17'
WHEN age BETWEEN 18 AND 25 THEN '18-25'
ELSE '26+' END AS AgeGroup,
COUNT(*) AS total
FROM MyTable
GROUP BY AgeGroup
SELECT
COUNT(CASE WHEN `age` BETWEEN 13 AND 17 THEN 1 END) `13-17`,
COUNT(CASE WHEN `age` BETWEEN 18 AND 25 THEN 2 END) `18-25`,
COUNT(CASE WHEN `age` > 25 THEN 3 END) `> 25`
FROM tableListOfAges;