Creating Range Buckets of column - sql

I am having one base table named test_table on which I am doing calculation of the age and it is coming as per expectation.
Here is the query I am using for age calculation.
select acol,
DATEDIFF(hour,CONVERT(DATEADD('SECOND', (epoch_time)/1000, DATE '1970-01-01'), DATE), CURRENT_TIMESTAMP()) AS age
from test_table;
But now I want to create different range of age column which will be displayed as a result by modifying the existing query.
Range would be :
1000-2000
2000-3000
3000-4000
ACOL AGE
MAG 1168
MAG 2168
MAG 3168
MAG 1100
MAG 2168
PNB 1672
MUM 1600
MUM 2696
MUM 3696
MUM 1696
Result after successful query has to look like below table.
ACOL 1000-2000 2000-3000 3000-4000
MAG 2 2 1
PNB 1 0 0
MUM 2 1 1
I know that we can do this using select case but not able to build the working query.
can anyone help me in this.
thanks in advance.

You can use conditional aggregation with a CASE expression:
SELECT t.ACOL,
SUM(CASE WHEN t.AGE >= 1000 AND t.AGE < 2000 THEN t.AGE ELSE 0 END) AS `1000-2000`,
SUM(CASE WHEN t.AGE < 3000 THEN t.AGE ELSE 0 END) AS `2000-3000`,
SUM(CASE WHEN t.AGE < 3000 AND t.AGE < 4000 THEN t.AGE ELSE 0 END) AS `3000-4000`
FROM
(
SELECT ACOL,
DATEDIFF(HOUR, CONVERT(DATEADD(SECOND, (epoch_time)/1000, DATE '1970-01-01'), DATE), CURRENT_TIMESTAMP()) AS AGE
FROM test_table
) t
GROUP BY t.ACOL

SELECT ACOL,
COUNT(CASE WHEN AGE >= 1000 AND AGE < 2000 THEN AGE ELSE NULL END) AS `1000-2000`,
COUNT(CASE WHEN AGE >= 2000 AGE < 3000 THEN AGE ELSE NULL END) AS `2000-3000`,
COUNT(CASE WHEN AGE >= 3000 AND AGE < 4000 THEN AGE ELSE NULL END) AS `3000-4000`
FROM (select ACOL,DATEDIFF(hour,CONVERT(DATEADD('SECOND', (epoch_time)/1000, DATE '1970-01-01'), DATE), CURRENT_TIMESTAMP()) AS AGE FROM test_table )
GROUP BY ACOL
Modified the from clause...
Working ...
:)

Related

Count average with multiple conditions

I'm trying to create a query which allows to categorize the average percentage for specific data per month.
Here's how my dataset presents itself:
Date
Name
Group
Percent
2022-01-21
name1
gr1
5.2
2022-01-22
name1
gr1
6.1
2022-01-26
name1
gr1
4.9
2022-02-01
name1
gr1
3.2
2022-02-03
name1
gr1
8.1
2022-01-22
name2
gr1
36.1
2022-01-25
name2
gr1
32.1
2022-02-10
name2
gr1
35.8
...
...
...
...
And here's what I want to obtain with my query (based on what I showed of the table):
Month
<=25%
25<_<=50%
50<_<=75%
75<_<=100%
01
1
1
0
0
02
1
1
0
0
...
...
...
...
...
The result needs to:
Be ordered by month
Have the average use for each name counted and categorized
So far I know how to get the average of the Percent value per Name:
SELECT Name,
AVG(Percent)
from `table`
where Group = 'gr1'
group by Name
and how to count iterations of Percent in the categories created for the query:
SELECT EXTRACT(MONTH FROM Date) as Month,
COUNT(CASE WHEN Percent <= 25 AND Group = 'gr1' THEN Name END) `_25`,
COUNT(CASE WHEN Percent > 25 AND Percent <= 50 AND Group = 'gr1' THEN Name END) `_50`,
COUNT(CASE WHEN Percent > 50 AND Percent <= 75 AND Group = 'gr1' THEN Name END) `_75`,
COUNT(CASE WHEN Percent > 75 AND Percent <= 100 AND Group = 'gr1' THEN Name END) `_100`,
FROM `table`
GROUP BY Month
ORDER BY Month
but this counts all iterations of every name where I want the average of those values.
I've been struggling to figure out how to combine the two queries or to create a new one that answers my need.
I'm working with the BigQuery service from Google Cloud
This query produces the needed result, based on your example. So basically this combines your 2 queries using subquery, where the subquery is responsible to calculate AVG grouped by Name, Month and Group, and the outer query is for COUNT and "categorization"
SELECT
Month,
COUNT(CASE
WHEN avg <= 25 THEN Name
END) AS _25,
COUNT(CASE
WHEN avg > 25
AND avg <= 50 THEN Name
END) AS _50,
COUNT(CASE
WHEN avg > 50
AND avg <= 75 THEN Name
END) AS _75,
COUNT(CASE
WHEN avg > 75
AND avg <= 100 THEN Name
END) AS _100
FROM
(
SELECT
EXTRACT(MONTH from Date) AS Month,
Name,
AVG(Percent) AS avg
FROM
table1
GROUP BY Month, Name, Group
HAVING Group = 'gr1'
) AS namegr
GROUP BY Month
This is the result:
Month
_25
_50
_75
_100
1
1
1
0
0
2
1
1
0
0
See also Fiddle (BUT on MySql) - http://sqlfiddle.com/#!9/16c5882/9
You can use this query to Group By Month and each Name
SELECT CONCAT(EXTRACT(MONTH FROM Date), ', ', Name) AS DateAndName,
CASE
WHEN AVG(Percent) <= 25 THEN '1'
ELSE '0'
END AS '<=25%',
CASE
WHEN AVG(Percent) > 25 AND AVG(Percent) <= 50 THEN '1'
ELSE '0'
END AS '25<_<=50%',
CASE
WHEN AVG(Percent) > 50 AND AVG(Percent) <= 75 THEN '1'
ELSE '0'
END AS '50<_<=75%',
CASE
WHEN AVG(Percent) > 75 AND AVG(Percent) <= 100 THEN '1'
ELSE '0'
END AS '75<_<=100%'
from DataTable /*change to your table name*/
group by EXTRACT(MONTH FROM Date), Name
order by DateAndName
It gives the following result:
DateAndName
<=25%
25<_<=50%
50<_<=75%
75<_<=100%
1, name1
1
0
0
0
1, name2
0
1
0
0
2, name1
1
0
0
0
2, name2
0
1
0
0

how to calculate age from Date of birth and how to group the records by age ranges

I have a table with a column 'DOB', I need to group the records by age ranges. but i don't know how to calculate age using DOB and use case along with it. I got that code from this site.
select
case
when age_c <1 then 'Under 1,
when age_c between 1 and 5 then '1-5',
when age_c between 5 and 15 then '5-15',
when age_c between 15 and 35 then '15-35'
What I want is:
Under 1 1
1-5 15
15-35 54
You presumably want some kind of aggregation query here:
SELECT CASE WHEN age_c < 1 THEN 'Under 1'
WHEN age_c < 5 THEN '1 - 5'
WHEN age_c < 15 THEN '5 - 15'
WHEN age_c < 35 THEN '15 - 35' END AS age_range,
COUNT(*) AS cnt
FROM yourTable
GROUP BY 1;
WITH AgeData as
(
SELECT
cast(DATEDIFF(DAY,DOB,GETDATE())/365.25 as int) AS [AGE]
FROM your_table
),
GroupAge AS
(
SELECT [Age],
CASE
WHEN AGE < 1 THEN 'Under 1',
WHEN AGE BETWEEN 1 AND 5 THEN '1-5',
WHEN AGE BETWEEN 5 AND 15 THEN '5-15',
WHEN AGE BETWEEN 15 AND 35 THEN '15-35'
WHEN AGE > 35 THEN 'Over 35'
END AS [Age Groups]
FROM AgeData
)
SELECT COUNT(*) AS [callGrpCount],
[Age Groups] FROM GroupAge
GROUP BY [Age Groups] order by [Age Groups]
Assume: DOB in yyyymmdd format

How to group Ages with case

Someone knows how i can group ages witch the CASE statement?
I got the ages 14,15,16,17,18,19,20,21,22,23,24,25. I want groups like <18,19,20,>21
I started like:
Case age when BETWEEN 14 AND 18 THEN sum(age)
when >=21 THEN sum(age)
ELSE age END as age_groups
But i think aggregate functions don't work in CASE statements, at least it brings up an error.
CASE statement would work, you have a syntax error, your query would be:
SELECT Case when age BETWEEN 14 AND 18 THEN '14 - 18'
when age >=21 THEN '>=21'
ELSE age END as age_groups, SUM(age)
FROM YOUR_TABLE
GROUP BY Case when age BETWEEN 14 AND 18 THEN '14 - 18'
when age >=21 THEN '>=21'
ELSE age END
Use a CTE to calculate the age_groups, and then you can normally sum the ages for each group.
WITH age_groups AS (
SELECT Age,
Case when age BETWEEN 14 AND 18 THEN '14 - 18'
when age >=21 THEN '>=21'
ELSE age END as age_group
FROM YOUR_TABLE
)
SELECT age_group, sum(Age)
FROM age_groups
GROUP BY age_group
Another neat alternative is to place the CASE inside a CROSS APPLY (VALUES so you can then refer to it in other parts of the query without repetition:
SELECT
v.age_group,
SUM(Age)
FROM age_groups ag
CROSS APPLY (VALUES (
CASE WHEN ag.age BETWEEN 14 AND 18 THEN '14 - 18'
WHEN ag.age >= 21 THEN '>=21'
ELSE CAST(ag.age AS varchar(10)) END
) ) v(age_group)
GROUP BY v.age_group
You can use Case inside Sum():
select
Sum(Case when age < 18 then 1 end) '<18',
Sum(Case when age = 19 then 1 end) '19',
Sum(Case when age = 20 then 1 end) '20',
Sum(Case when age > 20 then 1 end) '>20'
from myTable;
DBFiddle demo is here
EDIT: If you meant to get the results vertically, still keep it simple:
select '<18' as ageGroup, Sum(Case when age < 18 then 1 end) total from myTable
union
select '19', Sum(Case when age = 19 then 1 end) from myTable
union
select '20', Sum(Case when age = 20 then 1 end) from myTable
union
select '20>', Sum(Case when age > 20 then 1 end) from myTable;

SQL query to group by age range from date created

I want to get statistics with sql query. My table is like this:
ID MATERIAL CREATEDATE DEPARTMENT
1 M1 10.10.1980 D1
2 M2 11.02.1970 D2
2 M3 18.04.1971 D3
.....................
.....................
.....................
How can I get a range of data count like this
DEPARTMENT AGE<10 10<AGE<20 20<AGE
D1 24 123 324
D2 24 123 324
Assuming that CREATEDATE is a date column, in PostgreSQL you can use the AGE function:
select DEPARTMENT, age(CREATEDATE) as AGE
from Materials
and with date_part you can get the age in years. To show the data in the format that you want, you could use this GROUP BY query:
select
DEPARTMENT,
sum(case when date_part('year', age(CREATEDATE))<10 then 1 end) as "age<10",
sum(case when date_part('year', age(CREATEDATE))>=10 and date_part('year', age(CREATEDATE))<20 then 1 end) as "10<age<20",
sum(case when date_part('year', age(CREATEDATE))>=20 then 1 end) as "20<age"
from
Materials
group by
DEPARTMENT
which can be simplified as:
with mat_age as (
select DEPARTMENT, date_part('year', age(CREATEDATE)) as mage
from Materials
)
select
DEPARTMENT,
sum(case when mage<10 then 1 end) as "age<10",
sum(case when mage>=10 and mage<20 then 1 end) as "10<age<20",
sum(case when mage>=20 then 1 end) as "20<age"
from
mat_age
group by
DEPARTMENT;
if you are using PostgreSQL 9.4 you can use FILTER:
with mat_age as (
select DEPARTMENT, date_part('year', age(CREATEDATE)) as mage
from Materials
)
select
DEPARTMENT,
count(*) filter (where mage<10) as "age<10",
count(*) filter (where mage>=10 and mage<20) as "10<age<20",
count(*) filter (where mage>=20) as "20<age"
from
mat_age
group by
DEPARTMENT;
The following solution assumes that your CREATEDATE column exists as some sort of valid Postgres date type. If this be not the case, and it is being stored as text, you will first have to convert it to date in order for the query to work.
SELECT DEPARTMENT,
SUM(CASE WHEN DATEDIFF(year, CREATEDATE, now()::date) < 10 THEN 1 ELSE 0 END) AS "AGE<10",
SUM(CASE WHEN DATEDIFF(year, CREATEDATE, now()::date) >= 10 AND
DATEDIFF(year, CREATEDATE, now()::date) < 20 THEN 1 ELSE 0 END) AS "10<AGE<20",
SUM(CASE WHEN DATEDIFF(year, CREATEDATE, now()::date) >= 20 THEN 1 ELSE 0 END) AS "20<AGE"
FROM Materials
GROUP BY DEPARTMENT
You can use extract(year FROM age(createdate)) to get the exact age
i.e
select extract(year FROM age(timestamp '01-01-1989')) age
will give you
Result:
age
---
27
so you can use following select statement to get your desired output:
SELECT dept
,sum(CASE WHEN age < 10THEN 1 END) "age<10"
,sum(CASE WHEN age >= 10 AND age < 20 THEN 1 END) "10<age<20"
,sum(CASE WHEN age >= 20 THEN 1 END) "20<age"
FROM (
SELECT dept,extract(year FROM age(crdate)) age
FROM dt
) t
GROUP BY dept
If you don't want to use a sub select use this.
SELECT dept
,sum(CASE WHEN extract(year FROM age(crdate)) < 10THEN 1 END) "age<10"
,sum(CASE WHEN extract(year FROM age(crdate)) >= 10 AND extract(year FROM age(crdate)) < 20 THEN 1 END) "10<age<20"
,sum(CASE WHEN extract(year FROM age(crdate)) >= 20 THEN 1 END) "20<age"
FROM dt
GROUP BY dept

Age intervals as row statistics with SQL

I'm trying to create this summary statistic table, counting the number of individuals in each category
Agegroup | All | Female | Male
------------------------------------------------
All | 560594 | 34324 | 234244
< 20 | 4324 | 545 | 3456
20 - 30 | 76766 | 3424 | 32428
30 - 40 | 36766 | 764 | 82427
40 - 50 | 46766 | 4324 | 72422
50 - 60 | 66766 | 3424 | 52424
> 60 | 76766 | 43424 | 12423
from this table
PersonID | Age | Sex
----------------------------
A | 43 | F
B | 22 | F
C | 65 | M
D | 33 | F
E | 28 | M
Is this even possible "in one go" with SQL? I experimented with this, but it's not really coming together..
SELECT SUM(CASE WHEN Age < 20 THEN 1 ELSE 0 END) AS [Under 20],
SUM(CASE WHEN Age BETWEEN 20 AND 30 THEN 1 ELSE 0 END) AS [20-30],
SUM(CASE WHEN Age BETWEEN 30 AND 40 THEN 1 ELSE 0 END) AS [30-40]
FROM Persons
I believe the below is the simplest way to achieve this, and also get the row back even if there are no people within that age range. Also since Sex only has 2 possible values you can use NULLIF instead of the case expression.
SELECT [Agegroup] = Name,
[All] = COUNT(Person.PersonID),
[Female] = COUNT(NULLIF(Person.Sex, 'M')),
[Male] = COUNT(NULLIF(Person.Sex, 'F'))
FROM (VALUES
(0, 1000, 'All'),
(0, 20, '< 20'),
(20, 30, '20 - 30'),
(30, 40, '30 - 40'),
(40, 50, '40 - 40'),
(50, 60, '50 - 40'),
(60, 1000, '> 60')
) AgeRange (MinValue, MaxValue, Name)
LEFT JOIN Person
ON Person.Age >= AgeRange.MinValue
AND Person.Age < AgeRange.Maxvalue
GROUP BY AgeRange.Name, AgeRange.MinValue, AgeRange.Maxvalue
ORDER BY AgeRange.MinValue, AgeRange.MaxValue DESC
Example on SQL Fiddle
Maybe something like this:
DECLARE #T TABLE(PersonID VARCHAR(5), Age INT,Sex VARCHAR(5))
INSERT INTO #T
VALUES
('A',43,'F'),
('B',22 ,'F'),
('C ',65,'M'),
('D',33,'F'),
('E',28,'M')
SQL
SELECT
'All' AS Agegroup,
COUNT(*) AS [All],
SUM(CASE WHEN tbl.Sex='F' THEN 1 ELSE 0 END) AS Female,
SUM(CASE WHEN tbl.Sex='M' THEN 1 ELSE 0 END) AS Male
FROM
#T AS tbl
UNION ALL
SELECT
tbl.Agegroup,
COUNT(*) AS [All],
SUM(CASE WHEN tbl.Sex='F' THEN 1 ELSE 0 END) AS Female,
SUM(CASE WHEN tbl.Sex='M' THEN 1 ELSE 0 END) AS Male
FROM
(
SELECT
(
CASE
WHEN Age BETWEEN 0 and 20
THEN '< 20'
WHEN Age BETWEEN 20 and 30
THEN '20 - 30'
WHEN Age BETWEEN 30 and 40
THEN '30 - 40'
WHEN Age BETWEEN 40 and 50
THEN '40 - 50'
WHEN Age BETWEEN 50 and 60
THEN '50 - 60'
WHEN Age> 60
THEN '> 60'
END
) AS Agegroup,
t.Age,
t.Sex
FROM
#T AS t
) AS tbl
GROUP BY
tbl.Agegroup
Your best pattern would be to create an age range table (or virtual table as in the example below) and join to it, then pivot the results to get your results into a columnar form.
select range as AgeGroup, m as Male, F as Female, m+f as [all]
from
(
select PersonID, range, sex
from
(
select 'all' as range, 0 as minval, 200 as maxval
union select '<20',0,19
union select '20-29',20,29
-- etc....
) ranges
left join
yourtable t
on t.age between minval and maxval
) src
pivot
(count(personid) for sex in ([m],[f])) p
try this:
;with Age_range as(
select '<20' as age union all
select '20 - 30' as age union all
select '30 - 40' as age union all
select '40 - 50' as age union all
select '50 - 60' as age union all
select '>60' as age
),
cte as(
select [Sex],
sum(case when [Age]<20 then 1 else 0 end) as '<20' ,
sum(case when [Age]between 20 and 29 then 1 else 0 end) as '20 - 30',
sum(case when [Age]between 30 and 39 then 1 else 0 end) as '30 - 40',
sum(case when [Age]between 40 and 49 then 1 else 0 end) as '40 - 50',
sum(case when [Age]between 50 and 59 then 1 else 0 end) as '50 - 60',
sum(case when [Age]>=60 then 1 else 0 end) as '>60'
from Persons
group by [Sex]),
cte1 as(select Sex,'<20' as cnt from cte where [<20]>0 union all
select Sex,'20 - 30' as cnt from cte where [20 - 30]>0 union all
select Sex,'30 - 40' as cnt from cte where [30 - 40]>0 union all
select Sex,'40 - 50' as cnt from cte where [40 - 50]>0 union all
select Sex,'50 - 60' as cnt from cte where [50 - 60]>0 union all
select Sex,'>60' as cnt from cte where [>60]>0)
select A.age,
COUNT(case when sex in ('M','F') then 1 end) as [All],
COUNT(case when sex='F' then 1 end) as Female,
COUNT(case when sex='M' then 1 end) as Male
from Age_range A left join cte1 C
on A.age=C.cnt
group by A.age
SQL Fiddle Demo
select 'All' as [Age Group]
. count(*) as [All],
, sum(case Sex when 'F' then 1 end) as Female
, sum(case Sex when 'M' then 1 end) as Male
from Persons
union all
select '< 20' as [Age Group]
. count(*) as [All],
, sum(case Sex when 'F' then 1 end) as Female
, sum(case Sex when 'M' then 1 end) as Male
from Persons
where Age < 20
union all
select '20 - 30' as [Age Group]
. count(*) as [All],
, sum(case Sex when 'F' then 1 end) as Female
, sum(case Sex when 'M' then 1 end) as Male
from Persons
where 20 <= Age and Age < 30
union all
...