How to use GROUP BY to calculate intervals - sql

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)

Related

How to use Group By for Range in PostgreSQL?

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.

Group subquery by different column

I have a sub query, where i generate the totals, by date employee_summary_arr.
After i get the records, i put them on a json array.
I want that the same query, to group by month date_truc('month', date_made) on employee_summary_arr_by_month column, but i cant figure an elegant way to do that without running it twice. Here is the SQL query:
SELECT
json_agg(tt) employee_summary_arr
--json_agg(tt) employee_summary_arr_by_month -- This is the same but in the group by should have date_trunc('month', date_made)
FROM (
SELECT
employee_id, "employeeID", first_name, last_name,
date_made,
SUM( CASE WHEN audit_code = 3 OR audit_code = 4 THEN amount ELSE 0 END) sales,
SUM( CASE WHEN audit_code = 25 THEN amount ELSE 0 END) sales_cog,
SUM( CASE WHEN audit_code = 34 THEN amount ELSE 0 END) layaways,
SUM( CASE WHEN audit_code = 35 THEN amount ELSE 0 END) layaways_cog,
SUM( CASE WHEN audit_code = 26 OR audit_code = 27 THEN amount ELSE 0 END) layaways_payments,
SUM( CASE WHEN audit_code = 30 OR audit_code = 31 THEN amount ELSE 0 END) layaways_paid,
SUM( CASE WHEN audit_code = 36 THEN amount ELSE 0 END) layaways_paid_cog,
SUM( CASE WHEN audit_code = 13 THEN amount ELSE 0 END) loans,
SUM( CASE WHEN audit_code = 7 THEN amount ELSE 0 END) loan_receipts,
SUM( CASE WHEN audit_code = 8 THEN amount ELSE 0 END) loan_finance_charge,
SUM( CASE WHEN audit_code = 14 THEN amount ELSE 0 END) purchases
FROM (
SELECT
TH.date_made,
COALESCE(SUM(TH.amount), 0) amount,
AC.description_on_reports,
AC.audit_code,
TH.employee_id, EMP."employeeID", EMP.first_name, EMP.last_name
FROM transaction_transactionhistory TH
LEFT JOIN people_employee EMP ON EMP.id = TH.employee_id
LEFT JOIN sms_admin_auditcode AC ON AC.id = TH.audit_code_id
WHERE TH.store_id = 25
--AND TH.employee_id = 84
AND AC.audit_code IN (3, 4, 25, 34, 35, 26, 27, 30, 31, 36, 13, 7, 8, 14)
GROUP BY TH.date_made, AC.description_on_reports, AC.audit_code,
TH.employee_id, EMP."employeeID", EMP.first_name, EMP.last_name
ORDER BY AC.audit_code
) t
GROUP BY employee_id, "employeeID", first_name, last_name, date_made
)tt
Not very elegant, but you can use windowed sum in your outermost select and do one extra SELECT on top of that for the final aggregations:
SELECT
json_agg((SELECT x FROM (SELECT ttt.employee_id, ttt.sales, [...]) AS x))
AS employee_summary_arr,
json_agg((SELECT x FROM (SELECT ttt.employee_id, ttt.sales_monthly, [...]) AS x))
AS employee_summary_arr_monthly
FROM
(
SELECT
[all the original columns...]
sum(sales) OVER (PARTITION BY employee_id, "employeeID", first_name, last_name, date_trunc('month', date_made))
AS sales_monthly,
[all the other monthly sums...]
FROM [your two inner-most table t and tt...]
) AS ttt

One case statement for multiple parameters

Below is my sample code situation
declare #Ka int = select count(empid) from employee where age between 18 and 22
declare #Kb int = select count(empid) from employee where age between 23 and 30
declare #Kc int = select count(empid) from employee where age between 31 and 35
Now, If Ka/Kb/Kc has some value other than 0 or null then it should display 'x' and if not it should display blank. So my select statement is:
select
CASE WHEN #Ka = 0 then ''
ELSE 'X'
END AS 'Ka',
CASE WHEN #Kb = 0 then ''
ELSE 'X'
END AS 'Kb',
CASE WHEN #Kc = 0 then ''
ELSE 'X'
END AS 'Kc'
So If I have around more parameters, should I do a case statement for each parameter as above? Is there a way to do single case statement including all the parameters obeying the condition? Or is there a simple way to accomplish something similar to this?
Why do it this way? It seems much more efficient if you were to do the following (without variables):
SELECT CASE WHEN COUNT(CASE WHEN age BETWEEN 18 AND 22 THEN empid END) = 0 THEN '' ELSE 'X' END AS Ka,
CASE WHEN COUNT(CASE WHEN age BETWEEN 23 AND 30 THEN empid END) = 0 THEN '' ELSE 'X' END AS Kb,
CASE WHEN COUNT(CASE WHEN age BETWEEN 31 AND 35 THEN empid END) = 0 THEN '' ELSE 'X' END AS Kc
FROM Employee;
No. Don't do this. You need the logic inside the query:
select sum(case when age between 18 and 22 then 1 else 0 end) as age_18_22,
sum(case when age between 23 and 30 then 1 else 0 end) as age_23_30,
sum(case when age between 31 and 35 then 1 else 0 end) as age_31_35
from employee e;
SQL doesn't work by substituting text in query strings. I mean, you can do that -- it is called dynamic sql. But you should first learn the basics of expressing queries.

how to show my data as in interval form in sql

I have a Table like that:
ID Class Marks
1 12th 0
2 10th 25
3 09th 24
4 12th 50
5 10th 60
6 09th 70
Desired Output Like that:-
Marks CLass12th class9th class10th
0-25 1 1 1
25-50 1 0 0
50-60 1 0 1
60-70 0 1 1
Total 3 2 3
how can i do same with sql
CREATE TABLE marks
(
id INT,
class VARCHAR(200),
marks INT
);
INSERT INTO marks
VALUES (1,
'12th',
0);
INSERT INTO marks
VALUES (1,
'10th',
25);
INSERT INTO marks
VALUES (1,
'9th',
24);
INSERT INTO marks
VALUES (1,
'12th',
50);
INSERT INTO marks
VALUES (1,
'10th',
60);
INSERT INTO marks
VALUES (1,
'9th',
70);
-----not able to put (0-24) condition in marks as it is actually minus the value as -24 -----
SELECT CASE
WHEN marks >= 0
AND marks < 25 THEN ( 024 )
WHEN marks >= 25
AND marks <= 50 THEN ( 2550 )
WHEN marks >= 51
AND marks < 60 THEN ( 5160 )
WHEN marks >= 60
AND marks < 71 THEN ( 6070 )
ELSE NULL
END AS marks,
Sum(class12th) AS CLass12th,
Sum(class10th) AS CLass9th,
Sum(class9th) AS CLass9th
FROM (SELECT id,
marks,
[12th] AS CLass12th,
[10th] AS CLass10th,
[9th] AS CLass9th
FROM (SELECT id,
class,
marks
FROM marks) AS SourceTable
PIVOT ( Count(class)
FOR class IN ([12th],
[10th],
[9th]) ) AS pivottable)a
GROUP BY marks
Try this :
select '0-25', sum(case when class = '12th' then 1 else 0 end) '12th',sum(case when class = '10th' then 1 else 0 end) '10th',sum(case when class = '9th' then 1 else 0 end) '9th'
from Yourtable where marks >= 0 and marks < = 25
union
select '25-50', sum(case when class = '12th' then 1 else 0 end),sum(case when class = '10th' then 1 else 0 end),sum(case when class = '9th' then 1 else 0 end)
from Yourtable where marks >= 25 and marks < = 50
union
select '50-60', sum(case when class = '12th' then 1 else 0 end),sum(case when class = '10th' then 1 else 0 end),sum(case when class = '9th' then 1 else 0 end)
from Yourtable where marks >= 50 and marks < = 60
union
select '60-70', sum(case when class = '12th' then 1 else 0 end),sum(case when class = '10th' then 1 else 0 end),sum(case when class = '9th' then 1 else 0 end)
from Yourtable where marks >= 60 and marks < = 70
union
select 'total', sum(case when class = '12th' then 1 else 0 end),sum(case when class = '10th' then 1 else 0 end),sum(case when class = '9th' then 1 else 0 end)
from Yourtable
You need to store your ranges our use a CTE and use it in a query like this:
;with ranges as (
select 0 fromMark, 25 toMark, '0-25' title
union all select 25,50, '25-50'
union all select 50,60, '50-60'
union all select 60,70, '60-70'
union all select 0,100, 'Total'
)
select
r.title,
count(case when t.Class = '12th' then 1 end) Class12th,
count(case when t.Class = '09th' then 1 end) Class9th,
count(case when t.Class = '10th' then 1 end) Class10th
from yourTable t
left join ranges r
on t.Marks >= r.fromMark and t.Marks < r.toMark
group by
r.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.