How to use Group By for Range in PostgreSQL? - sql

I have one table like below at AWS Timeseries:
tracking_id
measure_value::varchar
measure_value::bigint
measure_name
269
-
48
age
269
Male
-
gender
I want to filter it to genders and age range like:
age_range
male_count
female_count
0-12
15
12
13-17
25
17
I already have a query that:
WITH
genderTable AS (SELECT device_identifier, tracking_id, measure_value::varchar as gender FROM "comp_41"."Visqa18f" WHERE device_identifier='visdemo3' AND measure_name='gender' AND tracking_id IS NOT NULL AND time BETWEEN '2021-03-01 10:30:00' AND '2021-03-01 10:56:00' ORDER BY tracking_id),
ageTable AS (SELECT device_identifier, tracking_id, measure_value::bigint as age FROM "comp_41"."Visqa18f" WHERE device_identifier='visdemo3' AND measure_name='age' AND tracking_id IS NOT NULL
AND time BETWEEN '2021-03-01 10:30:00' AND '2021-03-01 10:56:00' ORDER BY tracking_id)
SELECT
SUM(CASE WHEN age > 0 AND age <= 12 AND gender = 'Male' THEN 1 ELSE 0 END) AS "0-12-M",
SUM(CASE WHEN age >= 13 AND age <= 17 AND gender = 'Male' THEN 1 ELSE 0 END) AS "13-17-M",
SUM(CASE WHEN age >= 18 AND age <= 24 AND gender = 'Male' THEN 1 ELSE 0 END) AS "18-24-M",
SUM(CASE WHEN age >= 25 AND age <= 34 AND gender = 'Male' THEN 1 ELSE 0 END) AS "25-34-M",
SUM(CASE WHEN age >= 35 AND age <= 44 AND gender = 'Male' THEN 1 ELSE 0 END) AS "35-44-M",
SUM(CASE WHEN age >= 45 AND age <= 54 AND gender = 'Male' THEN 1 ELSE 0 END) AS "45-54-M",
SUM(CASE WHEN age >= 55 AND gender = 'Male' THEN 1 ELSE 0 END) AS "54-M",
SUM(CASE WHEN age > 0 AND age <= 12 AND gender = 'Female' THEN 1 ELSE 0 END) AS "0-12-F",
SUM(CASE WHEN age >= 13 AND age <= 17 AND gender = 'Female' THEN 1 ELSE 0 END) AS "13-17-F",
SUM(CASE WHEN age >= 18 AND age <= 24 AND gender = 'Female' THEN 1 ELSE 0 END) AS "18-24-F",
SUM(CASE WHEN age >= 25 AND age <= 34 AND gender = 'Female' THEN 1 ELSE 0 END) AS "25-34-F",
SUM(CASE WHEN age >= 35 AND age <= 44 AND gender = 'Female' THEN 1 ELSE 0 END) AS "35-44-F",
SUM(CASE WHEN age >= 45 AND age <= 54 AND gender = 'Female' THEN 1 ELSE 0 END) AS "45-54-F",
SUM(CASE WHEN age >= 55 AND gender = 'Female' THEN 1 ELSE 0 END) AS "54-F"
FROM ageTable, genderTable WHERE ageTable.tracking_id = genderTable.tracking_id
I want to do it with shorter and better optimized query and decided to do it with GROUP BY for age range
How can I use GROUP BY for filter on AGE RANGE?

This should do it:
demo:db<>fiddle
SELECT
CASE
WHEN age BETWEEN 0 and 12 THEN '0 - 12'
WHEN age BETWEEN 13 and 17 THEN '13 - 17'
...
END as age_range,
COUNT(*) FILTER (WHERE gender = 'Male') as male_count,
COUNT(*) FILTER (WHERE gender = 'Female') as female_count
FROM (
SELECT
tracking_id,
MAX(measure_value_gender) as gender,
MAX(measure_value_age) as age
FROM mytable
GROUP BY tracking_id
) s
GROUP BY age_range
If you don't want to use the FILTER clause, you can, of course, use your SUM(CASE WHEN gender = 'Male' THEN 1 ELSE 0 END) construct as well.

Related

How to transpose columns?

I have this table
SKU
CITY
MOV
TYPE
UNI
IMP
1
116
49
Caducidad
3
203.889
1
116
48
Daño
3
203.889
1
116
47
Robo
NULL
NULL
And I'm trying to transpose 'Type' column to display something like this
SKU
CITY
TYPE_UNI_CADUCIDAD
TYPE_IMP_CADUCIDAD
TYPE_UNI_DAÑO
TYPE_IMP_DAÑO
TYPE_UNI_ROBO
TYPE_IMP_ROBO
1
116
3
203.889
3
203.889
NULL
NULL
I tried case and pivot but not really working
SELECT SKU, CITY,
case
when MOV=49 then sum(Total_Imp) end as Type_Imp_Caducidad ,
case
when MOV=48 then sum(Total_Imp) end as Type_Imp_Daño ,
case
when MOV=47 then sum(Total_Imp) end as Type_Imp_Robo
from #movimientos
where Id_Num_SKU=11466978
group by SKY, CITY, MOV
As both comentsalready told you you need to aggreate the hole CASE WHEN to have an aggregation function
SELECT SKU, CITY,
sum(case
when MOV=49 then Total_Imp end) as Type_Imp_Caducidad ,
sum(case
when MOV=48 then Total_Imp end) as Type_Imp_Daño ,
SUM(case
when MOV=47 then Total_Imp end) as Type_Imp_Robo
from #movimientos
where Id_Num_SKU=11466978
group by SKY, CITY
You need to remove MOV from the GROUP BY and then aggregate the whole CASE expression
SELECT
m.SKU,
m.CITY,
SUM(case when m.MOV = 49 then m.Total_Uni end) as Type_Uni_Caducidad,
SUM(case when m.MOV = 49 then m.Total_Imp end) as Type_Imp_Caducidad,
SUM(case when m.MOV = 48 then m.Total_Uni end) as Type_Uni_Daño,
SUM(case when m.MOV = 48 then m.Total_Imp end) as Type_Imp_Daño,
SUM(case when m.MOV = 47 then m.Total_Uni end) as Type_Uni_Robo,
SUM(case when m.MOV = 47 then m.Total_Imp end) as Type_Imp_Robo
from #movimientos m
where m.Id_Num_SKU = 11466978
group by
m.SKY,
m.CITY;

Getting Total Number of Fields from SQL Table and Plot to Chart Using VB.Net

I have table as shown below:
Scholar
ID Gender DateGranted
1 M 1/1/2014
2 F 1/2/2013
3 M 12/25/2015
4 M 3/31/2016
5 F 5/15/2015
so on. . . .
I would like to get this number of male and female (per year) and plot it on Chart.
MALE FEMALE
3 2
What I have done:
(1) SQL Query:
SELECT(case when s.Gender = 'M' then Count(s.gender) else 0 end) as [MALE],
(case when s.Gender = 'F' then Count(s.gender) else 0 end) as [FEMALE] FROM ScholarshipGrants s
WHERE s.DateGranted >= '1/1/2013' AND s.DateGranted
(2) Chart Code
With chart
.Series(0).Points.Clear()
.Series(1).Points.Clear()
.Series(0).Points.DataBind(b, "YEAR", "MALE", "Label=MALE")
.Series(1).Points.DataBind(b, "YEAR", "FEMALE", "Label=FEMALE")
.Series(0).Color = Color.Blue
.Series(1).Color = Color.HotPink
'.Series(0).ChartType = SeriesChartType.Bar
ZeroDataPoint(.Series(1))
.Update()
End With
Result:
Male are not counted
try change your query script:
SELECT SUM(case when s.Gender = 'M' then 1 else 0 end) as [MALE],
SUM(case when s.Gender = 'F' then 1 else 0 end) as [FEMALE]
FROM ScholarshipGrants s
WHERE s.DateGranted >= '1/1/2013' AND s.DateGranted
Use below query :-
SELECT
COUNT (case when s.Gender = 'M' then 1 else NULL end) as [MALE],
COUNT (case when s.Gender = 'F' then 1 else NULL end) as [FEMALE]
FROM ScholarshipGrants s
WHERE s.DateGranted >= '1/1/2013'

Getting sub-counts along with Average and Count

I have two tables...
MODULES ENROLMENTS
GroupNo StudentNo
Title GroupNo
Tutor CourseworkMark
DayNo ExamMark
Time
Room
Semester
I wish to create a view that displays the average mark achieved in coursework and exam for each module and also a count of the number of students who achieved >70, 60-69, 50-59, 40-49 and <40. Is this possible?
I have the average marks worked out with...
SELECT Title,
AVG(CourseworkMark) AS AverageCoursework,
AVG(ExamMark) AS AverageExam
FROM tblModules INNER JOIN tblEnrolments
ON tblModules.GroupNo = tblEnrolments.GroupNo
GROUP BY Title;
You can use a SUMmed CASE expression to do this;
SELECT Title,
AVG(CourseworkMark) AS AverageCoursework,
AVG(ExamMark) AS AverageExam,
SUM(CASE WHEN CourseworkMark > 70 THEN 1 ELSE 0 END) AS CourseworkMarkOver70,
SUM(CASE WHEN CourseworkMark BETWEEN 60 AND 70 THEN 1 ELSE 0 END) AS CourseworkMarkOver60To69,
SUM(CASE WHEN CourseworkMark BETWEEN 50 and 59 THEN 1 ELSE 0 END) AS CourseworkMarkOver50To59,
SUM(CASE WHEN CourseworkMark BETWEEN 40 and 49 THEN 1 ELSE 0 END) AS CourseworkMarkOver40To49,
SUM(CASE WHEN CourseworkMark < 40 THEN 1 ELSE 0 END) AS CourseworkMarkUnder40
FROM tblModules INNER JOIN tblEnrolments
ON tblModules.GroupNo = tblEnrolments.GroupNo
GROUP BY Title;

count(*) based on the gender condition

I have the following table in oracle10g.
state gender avg_sal status
NC M 5200 Single
OH F 3800 Married
AR M 8800 Married
AR F 6200 Single
TN M 4200 Single
NC F 4500 Single
I am trying to form the following report based on some condition. The report should look like the one below. I tried the below query but count(*) is not working as expected
state gender no.of males no.of females avg_sal_men avg_sal_women
NC M 10 0 5200 0
OH F 0 5 0 3800
AR M 16 0 8800 0
AR F 0 12 0 6200
TN M 22 0 4200 0
NC F 0 8 0 4500
I tried the following query but I am not able to count based onthe no.of males and no.of females..
select State, "NO_OF MALES", "$AVG_sal", "NO_OF_FEMALES", "$AVG_SAL_FEMALE"
from(
select State,
to_char(SUM((CASE WHEN gender = 'M' THEN average_price ELSE 0 END)),'$999,999,999') as "$Avg_sal_men,
to_char(SUM((CASE WHEN gender = 'F' THEN average_price ELSE 0 END)), '$999,999,999') as "$Avg_sal_women,
(select count (*) from table where gender='M')"NO_OF MALES",
(select count (*) from table where gender='F')"NO_OF_FEMALES"
from table group by State order by state);
You can use case as an expression (which you already know...). And the subquery is unnecessary.
select State
, sum(case gender when 'M' then 1 else 0 end) as "no.of males"
, sum(case gender when 'F' then 1 else 0 end) as "no.of females"
, to_char(
SUM(
(
CASE
WHEN gender = 'M' THEN average_price
ELSE 0
END
)
)
, '$999,999,999'
) as "Avg_sal_men",
to_char(SUM((CASE WHEN gender = 'F' THEN average_price ELSE 0 END))
,'$999,999,999'
) as "Avg_sal_women"
from table
group by State;
You are Conting by this sub-query select count (*) from table where gender='M' which always count the total number of male in your whole table....and you are doing same for counting female...
So you Can Try like this...
select State, "NO_OF MALES", "$AVG_sal", "NO_OF_FEMALES", "$AVG_SAL_FEMALE"
from(
select State,
to_char(SUM((CASE WHEN gender = 'M' THEN average_price ELSE 0 END)),'$999,999,999') as "$Avg_sal_men",
to_char(SUM((CASE WHEN gender = 'F' THEN average_price ELSE 0 END)), '$999,999,999') as "$Avg_sal_women,
Sum(Case when gender='M' then 1 else 0 end) "NO_OF MALES",
Sum(Case when gender='F' then 1 else 0 end) "NO_OF_FEMALES"
from table group by State order by state);
Try the following.
select state
,sum(case when gender = 'M' then 1 else 0 end) as nof_males
,sum(case when gender = 'F' then 1 else 0 end) as nof_females
,avg(case when gender = 'M' then average_price end) as avg_sal_male
,avg(case when gender = 'F' then average_price end) as avg_sal_female
from table
group
by state;
..add formatting as required.

How to use GROUP BY to calculate intervals

please consider this image:
I have a table like this:
Age Active Men/Women
-------------------------------------------------
I want to write a query that calulate Count of every age intervals for men and women.I don't know How I can use GROUP BY and intervals.
in my reports in left side of above image I want to calculate UnActive rows.How I can merge this to query to one query?
thanks
SELECT
Active.State,
Age.Base, Age.Base+4,
COUNT(*) AS TotalCount,
COUNT(CASE WHEN T.[Men/Women] = 'man' THEN 1 END) AS ManCount,
COUNT(CASE WHEN T.[Men/Women] = 'woman' THEN 1 END) AS WomanCount
FROM
(
VALUES (10),(15),(20),(25),(30),(35),(40) /*.. add the rest */,(90),(95)
) AS Age(base)
CROSS JOIN
(
VALUES (0), (1)
) AS Active(State)
LEFT JOIN
MyTable T ON T.Age BETWEEN Age.Base AND Age.Base+4 AND T.Active = Active.State
GROUP BY
Active.State,
Age.Base, Age.Base+4;
After this, you can format it how you want
Edited to generate empty ranges
You can GROUP BY on age interval CASEs:
SELECT
COUNT(*) AGE_COUNT,
CASE WHEN AGE BETWEEN 15 AND 19 THEN 'Age 15-19'
WHEN AGE BETWEEN 20 AND 24 THEN 'Age 20-24'
ELSE 'Other'
END AGE_INTERVAL
FROM
YOUR_TABLE
GROUP BY
CASE WHEN AGE BETWEEN 15 AND 19 THEN 'Age 15-19'
WHEN AGE BETWEEN 20 AND 24 THEN 'Age 20-24'
ELSE 'Other'
END
This should work:
SELECT SUM(CASE WHEN [Men/Women] = 'M' THEN 1 ELSE 0 END) AS Men,
SUM(CASE WHEN [Men/Women] = 'W' THEN 1 ELSE 0 END) AS Women,
COUNT(*) as Total,
CASE WHEN Age BETWEEN 10 AND 14 THEN 'Age 10-14'
WHEN Age BETWEEN 15 AND 19 THEN 'Age 15-19'
WHEN Age BETWEEN 20 AND 24 THEN 'Age 20-24'
ELSE 'Old'
END Age
FROM MyTable
WHERE Active = 1
GROUP BY CASE WHEN Age BETWEEN 10 AND 14 THEN 'Age 10-14'
WHEN Age BETWEEN 15 AND 19 THEN 'Age 15-19'
WHEN Age BETWEEN 20 AND 24 THEN 'Age 20-24'
ELSE 'Old'
END
You can actually expand on from what Andrea provided...
SELECT
CASE WHEN YT.AGE BETWEEN 15 AND 19 THEN 'Age 15-19'
WHEN YT.AGE BETWEEN 20 AND 24 THEN 'Age 20-24'
ELSE 'Other'
END AGE_INTERVAL,
SUM( CASE WHEN YT.Active THEN 1 ELSE 0 END ) as ActiveCount,
SUM( CASE WHEN YT.Gender = 'M' THEN 1 ELSE 0 END ) as MaleCount,
SUM( CASE WHEN YT.Gender = 'F' THEN 1 ELSE 0 END ) as FemaleCount,
COUNT(*) AgeGroupCount
FROM
YourTable YT
GROUP BY
Age_Interval
As others have mentioned this is trying to report on a way that is not the same as your data at all.
But I think the best way achieve this within SQL is to create a scalar function (props to #gbn for the min/max age range logic):
CREATE FUNCTION usvf_calculate_age_bracket
(
-- Add the parameters for the function here
#age INT
)
RETURNS VARCHAR(50)
AS
BEGIN
-- Return the result of the function
RETURN CAST(#age/5*5 AS VARCHAR(20)) + '-' + CAST(#age/5*5+4 AS VARCHAR(20))
END
GO
Then in your query you can do:
SELECT SUM(CASE WHEN gender = 'F' THEN active ELSE 0 END) AS [Women],
SUM(CASE WHEN gender = 'M' THEN active ELSE 0 END) AS [Men],
SUM(CAST(active AS int)) AS [Total],
'Age ' + dbo.usvf_calculate_age_bracket(age) AS [Age]
FROM myTable
GROUP BY dbo.usvf_calculate_age_bracket(age)
My test code:
DECLARE #T TABLE
(
age int,
active bit,
gender char(1)
)
INSERT INTO #T VALUES (25, 1, 'M'),(32, 0, 'F'),(21, 1, 'M'),(22, 1, 'F'),(28, 1, 'F'),(32, 0, 'M'), (23, 1, 'M'),(42, 0, 'F'),(29, 1, 'M'),(29, 1, 'F'),(28, 1, 'F'),(32, 1, 'M')
SELECT SUM(CASE WHEN gender = 'F' THEN active ELSE 0 END) AS [Women],
SUM(CASE WHEN gender = 'M' THEN active ELSE 0 END) AS [Men],
SUM(CAST(active AS int)) AS [Total],
'Age ' + dbo.usvf_calculate_age_bracket(age) AS [Age] FROM #T
GROUP BY dbo.usvf_calculate_age_bracket(age)
Results:
Women Men Total Age
1 2 3 Age 20-24
3 2 5 Age 25-29
0 1 1 Age 30-34
0 0 0 Age 40-44
Which seems to be close to what you want, and it isn't an overly complicated SQL query to achieve this. (except I can't work out your 'total' column)