count a grouped by and cased mysql - mysql

Im trying to get the age of each person by computing on their birthday. Now I need to group them which I already achieved. Now what I want is to count how many in group 20 and below and so forth and so on.
Here's my query
select case when datediff(now(), birthday) / 365.25 > 50 then '51 & over' when datediff(now(), birthday) / 365.25 > 30 then '31 - 50' when datediff(now(), birthday) / 365.25 > 19 then '20 - 30' else 'under 20' end as age_group from members
The result is
Where I tried to group it by the query below
select case when datediff(now(), birthday) / 365.25 > 50 then '51 & over' when datediff(now(), birthday) / 365.25 > 30 then '31 - 50' when datediff(now(), birthday) / 365.25 > 19 then '20 - 30' else 'under 20' end as age_group from members group by age_group
Result will be
But what I need is somethinglike this
**NOte: Photo edited.
How do I achieved that I need to place the count result in a chart.

Wrap the query similar to this:
Select age_group, count(*)
From (
// your original query here
) t
Group by age_group

Just add a count(*) to your last query:
select case when datediff(now(), birthday) / 365.25 > 50 then '51 & over' when datediff(now(), birthday) / 365.25 > 30 then '31 - 50' when datediff(now(), birthday) / 365.25 > 19 then '20 - 30' else 'under 20' end as age_group, count(*) from members group by age_group

SELECT
concat(
datediff(now(), birthday)/365.25 div 20 * 20 + 1,
'-',
(datediff(now(), birthday)/365.25 div 20 + 1) * 20) AS age,
count(*)
FROM members
GROUP BY datediff(now(), birthday)/365.25 DIV 20
This query will give the result in the following format
+-------+----------+
| age | count(*) |
+-------+----------+
| 1-20 | 2 |
| 21-40 | 1 |
| 41-60 | 1 |
+-------+----------+
Based on the following table definition and sample data:
CREATE TABLE `stack` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`birthdate` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into members values (null,'1973-10-02');
insert into members values (null,'1977-04-07');
insert into members values (null,'2006-07-03');
insert into members values (null,'2011-10-11');

Related

Mysql add an extra column at end instead of using a union

I am trying to combine two queries so the data shows up in one table. I am using a union to combine the two queries. However, everything is added to the same column, what do I change so the results from the different queries take up a new column.
Here is an image of the query result.
Here is my code
select * from(
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
COUNT(*) AS count,
CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
FROM (SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users
join subscriptions on users.id = subscriptions.user_id
where users.plan <> 'domain' and users.plan <> '' and users.plan <> 'domain_cpi' and users.birthdate_on is not null
) as derived
GROUP BY age_range
union
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range2,
COUNT(*) AS count2,
CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
FROM (SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users) as derived2
GROUP BY age_range2
) as test2
ORDER BY ordinal
I want the result so only one under 25 shows, but the two results for under 25 493 and 2046 are in different columns. Same for all other ranges
Sounds like you want to put a JOIN to derived.age_range ON test2.age_range2
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal,
count, count2
FROM (
SELECT
derived.age,
COUNT(*) AS count
FROM (
SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users
join subscriptions on users.id = subscriptions.user_id
where users.plan <> 'domain' and users.plan <> '' and users.plan <> 'domain_cpi' and users.birthdate_on is not null
GROUP BY age
) as derived
JOIN
SELECT
derived2.age,
COUNT(*) AS count2
FROM (
SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users
GROUP BY age
) as derived2
ON derived.age = derived2.age
)
ORDER BY ordinal ASC;
I don't believe you need 2 queries just a left join instead. The count() function ONLY increments for non-null values so you can have users counted even if they don't meet the subscription criteria.
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range
, CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
, COUNT(DISTINCT id) AS user_count # distinct might not be needed
, COUNT(subscriber_id) AS subscriber_count
FROM (
SELECT
users.id
, TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age
, subscriptions.user_id AS subscriber_id
FROM users
LEFT JOIN subscriptions ON users.id = subscriptions.user_id
AND users.plan <> 'domain'
AND users.plan <> ''
AND users.plan <> 'domain_cpi'
AND users.birthdate_on IS NOT NULL
) d
GROUP BY
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END
, CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END

How to use multiple group by conditions in mysql

I have a customer_master table. In that table I have two columns called customer_id and date_of_birth.
what I want is get count of customers group by their age ranger. Something like this.
So far this is the only query I could try.
select COUNT(customer_id) AS count FROM customer_master
WHERE (DATEDIFF( CURDATE(),date_of_birth) / 365.25)<40
Please help me out with this. Thank you.
With everyone's help I found a perfect answer than you all.
SELECT CASE
WHEN (DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 20 THEN 'Below 20'
WHEN(DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 30 THEN 'Below 30'
WHEN (DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 40 THEN 'Below 40'
WHEN (DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 50 THEN 'Below 50'
ELSE 'Over 50'
END as age_group,
COUNT(customer_id)
FROM customer_master
GROUP BY age_group;
You can use the CASE operator.
SELECT CASE
WHEN (DATEDIFF( CURDATE(),date_of_birth) / 365.25) < 40 THEN 'Below 40'
ELSE 'Over 40'
END as age_group,
COUNT(customer_id)
FROM customer_master
GROUP BY age_group;
Sorry for poor formatting, it is my first answer
One solution would be to use cascading values with CASE within a subquery:
select age_group, count(customer_id) as 'count' from
(select customer_id,
year(curdate())-year(date_of_birth) as 'age',
case when (year(curdate())-year(date_of_birth)) < 20, "Below 20"
when (year(curdate())-year(date_of_birth)) < 30, "Between 20 and 29"
when (year(curdate())-year(date_of_birth)) < 40, "Between 30 and 39"
else "40 or Greater" end as 'age_group'
FROM customer_master) x
group by age_group
SELECT
(year(curdate())-year(date_of_birth)) div 20 as age_group,
COUNT(customer_id)
FROM
customer_master
GROUP BY age_group
Something like this will give you number of customers in every 20. If you want different size of group just change the number you divide by.
That is assuming you want each group to be same size e.g
1 - 20
21 - 40
41 - 60
...
If you want different sizes go with CASE solution as other have suggested.

How can I JOIN static values and use them as GROUP BY identifier?

I have created a SELECT statement where I fetch the time difference from now to a certain date in my database.
SELECT
`username`,
TIMESTAMPDIFF(HOUR, `a_date`, NOW()) AS `timediff`
FROM
`User`
... results in something like
username | timediff
-------------------
john | 441
henry | 1624
mike | 4
kyle | NULL
Now I would like to group them into pre defined groups, e.g.
NULL
less than 20
greater than 20
How can I JOIN the above result with a static set of predefined values and how can I use those values as my grouping identifier?
group | amount
--------------
NULL | 1
lt 20 | 1
gt 20 | 2
You can use case statement with group by:
SELECT (CASE WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) < 20 THEN 'lt 20'
WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) >= 20 THEN 'gt 20'
END) as grp, COUNT(*)
FROM `User`
GROUP BY grp;
As a side note: I usually repeat the case statement in the group by, because many databases don't allow column aliases there:
SELECT (CASE WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) < 20 THEN 'lt 20'
WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) >= 20 THEN 'gt 20'
END) as grp, COUNT(*)
FROM `User`
GROUP BY (CASE WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) < 20 THEN 'lt 20'
WHEN TIMESTAMPDIFF(HOUR, `a_date`, NOW()) >= 20 THEN 'gt 20'
END);

How to calculate age from date of birth and group each member into age range in sql

I have an sql table that stores people's details i.e id, name, DoB, registration_date and address. I would like to calculate the age of each individual and then group them into these ranges: 20-30, 31-50, 51 & over.
I know i can get the age by doing: (https://stackoverflow.com/a/1572257/3045800)
SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
I just need to figure out how to group all people into thier respective range.
Thanks for the help
Use a case to produce the age group description:
select *,
case
when datediff(now(), date_of_birth) / 365.25 > 50 then '51 & over'
when datediff(now(), date_of_birth) / 365.25 > 30 then '31 - 50'
when datediff(now(), date_of_birth) / 365.25 > 19 then '20 - 30'
else 'under 20'
end as age_group
from person
Note the simpler way to calculate age.
You can use with construction:
with Query as (
select FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age
... -- Other fields
from MyTable
)
select case
-- whatever ranges you want
when (Age < 20) then
1
when (Age >= 20) and (Age <= 30) then
2
when (Age > 30) and (Age <= 50) then
3
else
4
end AgeRange,
...
from Query
group by AgeRange
I was once faced with the same requirement and this is How I fixed it: I wish there was a Straight Forward way as this is Not:
SELECT (CASE
WHEN G.DATE_OF_BIRTH IS NULL
THEN
'18-24' --Put your default Range In case the date of birth is null
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (YEAR FROM g.DATE_OF_BIRTH) < 18
THEN
'MINORS'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (YEAR FROM g.DATE_OF_BIRTH) BETWEEN 25
AND 29
THEN
'25-29'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (YEAR FROM g.DATE_OF_BIRTH) BETWEEN 30
AND 34
THEN
'30-34'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (
YEAR FROM g.DATE_OF_BIRTH) BETWEEN 35
AND 39
THEN
'35-39'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (
YEAR FROM g.DATE_OF_BIRTH) BETWEEN 40
AND 49
THEN
'40-49'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (
YEAR FROM g.DATE_OF_BIRTH) BETWEEN 50
AND 59
THEN
'50-59'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (
YEAR FROM g.DATE_OF_BIRTH) BETWEEN 60
AND 69
THEN
'60-69'
ELSE
CASE
WHEN EXTRACT (
YEAR FROM (select sysdate from dual))
- EXTRACT (
YEAR FROM g.DATE_OF_BIRTH) >=
70
THEN
'ELDERLY'
END
END
END
END
END
END
END
END
END) from your table g
This is just example. Replace the ranges with your Preferred Ones. This I have done using Oracle.

MySQL GROUP BY age range including null ranges

I'm trying to count the number of people by age ranges, and I can almost do it with 2 problems:
If there are no people in a given age range (NULL), then that age range does not appear in the results. For example, in my data there's no entries for "Over 80" so that date range does not appear. Basically, it looks like a mistake in the programming when there are missing date ranges.
I'd like to order the results in a specific way. In the query below, because the ORDER BY is by age_range, the results for '20 - 29' come before the results for 'Under 20'.
Here's a sample of the db table "inquiries":
inquiry_id birth_date
1 1960-02-01
2 1962-03-04
3 1970-03-08
4 1980-03-02
5 1990-02-08
Here's the query:
SELECT
CASE
WHEN age < 20 THEN 'Under 20'
WHEN age BETWEEN 20 and 29 THEN '20 - 29'
WHEN age BETWEEN 30 and 39 THEN '30 - 39'
WHEN age BETWEEN 40 and 49 THEN '40 - 49'
WHEN age BETWEEN 50 and 59 THEN '50 - 59'
WHEN age BETWEEN 60 and 69 THEN '60 - 69'
WHEN age BETWEEN 70 and 79 THEN '70 - 79'
WHEN age >= 80 THEN 'Over 80'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
COUNT(*) AS count
FROM (SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age FROM inquiries) as derived
GROUP BY age_range
ORDER BY age_range
Here's a simple solution based on the suggestion by Wrikken:
SELECT
SUM(IF(age < 20,1,0)) as 'Under 20',
SUM(IF(age BETWEEN 20 and 29,1,0)) as '20 - 29',
SUM(IF(age BETWEEN 30 and 39,1,0)) as '30 - 39',
SUM(IF(age BETWEEN 40 and 49,1,0)) as '40 - 49',
SUM(IF(age BETWEEN 50 and 59,1,0)) as '50 - 59',
SUM(IF(age BETWEEN 60 and 69,1,0)) as '60 - 69',
SUM(IF(age BETWEEN 70 and 79,1,0)) as '70 - 79',
SUM(IF(age >=80, 1, 0)) as 'Over 80',
SUM(IF(age IS NULL, 1, 0)) as 'Not Filled In (NULL)'
FROM (SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age FROM inquiries) as derived
An alternative to the range table (which has my preference), a single-row answer could be:
SELECT
SUM(IF(age < 20,1,0)) as 'Under 20',
SUM(IF(age BETWEEN 20 and 29,1,0)) as '20 - 29',
SUM(IF(age BETWEEN 30 and 39,1,0)) as '30 - 39',
SUM(IF(age BETWEEN 40 and 49,1,0)) as '40 - 49',
...etc.
FROM inquiries;
One way of ordering the results would be introducing a column in the select statement and giving it a rank value of the way you want your results to be ordered with the rest and then order by that row, for example
SELECT
CASE
WHEN age < 20 THEN 'Under 20'
WHEN age BETWEEN 20 and 29 THEN '20 - 29'
WHEN age BETWEEN 30 and 39 THEN '30 - 39'
WHEN age BETWEEN 40 and 49 THEN '40 - 49'
WHEN age BETWEEN 50 and 59 THEN '50 - 59'
WHEN age BETWEEN 60 and 69 THEN '60 - 69'
WHEN age BETWEEN 70 and 79 THEN '70 - 79'
WHEN age >= 80 THEN 'Over 80'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
COUNT(*) AS count,
CASE
WHEN age < 20 THEN 1
WHEN age BETWEEN 20 and 29 THEN 2
WHEN age BETWEEN 30 and 39 THEN 3
WHEN age BETWEEN 40 and 49 THEN 4
WHEN age BETWEEN 50 and 59 THEN 5
WHEN age BETWEEN 60 and 69 THEN 6
WHEN age BETWEEN 70 and 79 THEN 7
WHEN age >= 80 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
FROM (SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age FROM inquiries) as derived
GROUP BY age_range
ORDER BY ordinal
Create a table that contains all ranges and use outer join.
Order by numeric value in another column of that table
SELECT range, ....
FROM ranges
LEFT JOIN (Your subquery) ON (ranges.range = your_range)
...
ORDER BY range.year ASC