I have data as below:
custid date gender cust_type
25309 29/10/2018 M A
25310 09/11/2018 F B
25311 10/11/2018 O C
25312 18/09/2018 F D
25313 18/09/2018 O A
25314 18/09/2018 M B
25315 18/09/2018 F C
25316 18/09/2018 F D
25317 19/09/2018 M D
25318 19/09/2018 O B
My final output should be as below:
quarter total A M F O TOTAL B M F O TOTAL C M F O TOTAL D M F O
2 1 1 3 1 1 1 2 0 1 1 3 1 2 0
I need the count of distinct customer for each cust_type.
Within each cust_type, i need the count of M,F,O (gender)
The output should be calculated for each quarter based on date column. I tried few suggestions in site, but its giving me wrong count, while using sum within case statement.
at present i m running seperate queries for each quarter to get cust_type count and gender count as below:
SELECT INDIVIDUAL_TYPE,COUNT(DISTINCT CUST_ID)
FROM TOT_POP_DET
WHERE DATE < (TO_DATE('01-JAN-2020','DD-MON-YYYY'))
GROUP BY CUST_TYPE
SELECT GENDER,COUNT(DISTINCT CUST_ID)
FROM TOT_POP_DET
WHERE DATE < (TO_DATE('01-JAN-2020','DD-MON-YYYY'))
AND CUST_TYPE='OTHER'
GROUP BY GENDER
Seeking help here.
Do a group by DATEPART(QUARTER, [Date Column]) then do SUM(CASE ...) for the individual rows you need to count.
Below is example using your example data.
Select
DATEPART(QUARTER, date) [Quarter],
SUM(CASE WHEN cust_type = 'A' THEN 1 ELSE 0) [Total A],
SUM(CASE WHEN cust_type = 'A' AND gender = 'M' THEN 1 ELSE 0) [A - Male],
SUM(CASE WHEN cust_type = 'A' AND gender = 'F' THEN 1 ELSE 0) [A - Female],
SUM(CASE WHEN cust_type = 'A' AND gender = 'O' THEN 1 ELSE 0) [A - Other],
SUM(CASE WHEN cust_type = 'B' THEN 1 ELSE 0) [Total B],
SUM(CASE WHEN cust_type = 'B' AND gender = 'M' THEN 1 ELSE 0) [B - Male],
SUM(CASE WHEN cust_type = 'B' AND gender = 'F' THEN 1 ELSE 0) [B - Female],
SUM(CASE WHEN cust_type = 'B' AND gender = 'O' THEN 1 ELSE 0) [B - Other],
SUM(CASE WHEN cust_type = 'C' THEN 1 ELSE 0) [Total C],
SUM(CASE WHEN cust_type = 'C' AND gender = 'M' THEN 1 ELSE 0) [C - Male],
SUM(CASE WHEN cust_type = 'C' AND gender = 'F' THEN 1 ELSE 0) [C - Female],
SUM(CASE WHEN cust_type = 'C' AND gender = 'O' THEN 1 ELSE 0) [C - Other],
SUM(CASE WHEN cust_type = 'D' THEN 1 ELSE 0) [Total D],
SUM(CASE WHEN cust_type = 'D' AND gender = 'M' THEN 1 ELSE 0) [D - Male],
SUM(CASE WHEN cust_type = 'D' AND gender = 'F' THEN 1 ELSE 0) [D - Female],
SUM(CASE WHEN cust_type = 'D' AND gender = 'O' THEN 1 ELSE 0) [D - Other]
FROM
TOT_POP_DET
WHERE
SNAPSHOT_DATE < (TO_DATE('01-JAN-2020','DD-MON-YYYY'))
GROUP BY
DATEPART(QUARTER, date)
Oracle Below
Select
TO_NUMBER(TO_CHAR(date, 'QUARTER')) [Quarter],
SUM(CASE WHEN cust_type = 'A' THEN 1 ELSE 0) [Total A],
SUM(CASE WHEN cust_type = 'A' AND gender = 'M' THEN 1 ELSE 0) [A - Male],
SUM(CASE WHEN cust_type = 'A' AND gender = 'F' THEN 1 ELSE 0) [A - Female],
SUM(CASE WHEN cust_type = 'A' AND gender = 'O' THEN 1 ELSE 0) [A - Other],
SUM(CASE WHEN cust_type = 'B' THEN 1 ELSE 0) [Total B],
SUM(CASE WHEN cust_type = 'B' AND gender = 'M' THEN 1 ELSE 0) [B - Male],
SUM(CASE WHEN cust_type = 'B' AND gender = 'F' THEN 1 ELSE 0) [B - Female],
SUM(CASE WHEN cust_type = 'B' AND gender = 'O' THEN 1 ELSE 0) [B - Other],
SUM(CASE WHEN cust_type = 'C' THEN 1 ELSE 0) [Total C],
SUM(CASE WHEN cust_type = 'C' AND gender = 'M' THEN 1 ELSE 0) [C - Male],
SUM(CASE WHEN cust_type = 'C' AND gender = 'F' THEN 1 ELSE 0) [C - Female],
SUM(CASE WHEN cust_type = 'C' AND gender = 'O' THEN 1 ELSE 0) [C - Other],
SUM(CASE WHEN cust_type = 'D' THEN 1 ELSE 0) [Total D],
SUM(CASE WHEN cust_type = 'D' AND gender = 'M' THEN 1 ELSE 0) [D - Male],
SUM(CASE WHEN cust_type = 'D' AND gender = 'F' THEN 1 ELSE 0) [D - Female],
SUM(CASE WHEN cust_type = 'D' AND gender = 'O' THEN 1 ELSE 0) [D - Other]
FROM
TOT_POP_DET
WHERE
SNAPSHOT_DATE < (TO_DATE('01-JAN-2020','DD-MON-YYYY'))
GROUP BY
TO_NUMBER(TO_CHAR(date, 'QUARTER'))
You didn't specify the logic for the QUARTER column so I just assumed you meant quarter of the calendar year (Jan-Mar = Q1, Apr-Jun = Q2, etc.). If you group the information by quarter and customer type, you can then pivot that information to get it in the format that you want.
Setup
create table cust_table as
select 25309 as custid , to_date('29/10/2018','dd/mm/yyyy') as date_val, 'M' as gender, 'A' as cust_type from dual union all
select 25310 as custid , to_date('09/11/2018','dd/mm/yyyy') as date_val, 'F' as gender, 'B' as cust_type from dual union all
select 25311 as custid , to_date('10/11/2018','dd/mm/yyyy') as date_val, 'O' as gender, 'C' as cust_type from dual union all
select 25312 as custid , to_date('18/09/2018','dd/mm/yyyy') as date_val, 'F' as gender, 'D' as cust_type from dual union all
select 25313 as custid , to_date('18/09/2018','dd/mm/yyyy') as date_val, 'O' as gender, 'A' as cust_type from dual union all
select 25314 as custid , to_date('18/09/2018','dd/mm/yyyy') as date_val, 'M' as gender, 'B' as cust_type from dual union all
select 25315 as custid , to_date('18/09/2018','dd/mm/yyyy') as date_val, 'F' as gender, 'C' as cust_type from dual union all
select 25316 as custid , to_date('18/09/2018','dd/mm/yyyy') as date_val, 'F' as gender, 'D' as cust_type from dual union all
select 25317 as custid , to_date('19/09/2018','dd/mm/yyyy') as date_val, 'M' as gender, 'D' as cust_type from dual union all
select 25318 as custid , to_date('19/09/2018','dd/mm/yyyy') as date_val, 'O' as gender, 'B' as cust_type from dual;
Query
SELECT year,
quarter,
NVL (a_m_total, 0)
+ NVL (a_f_total, 0)
+ NVL (a_o_total, 0)
+ NVL (b_m_total, 0)
+ NVL (b_f_total, 0)
+ NVL (b_o_total, 0)
+ NVL (c_m_total, 0)
+ NVL (c_f_total, 0)
+ NVL (c_o_total, 0)
+ NVL (d_m_total, 0)
+ NVL (d_f_total, 0)
+ NVL (d_o_total, 0) AS quarter_total,
NVL (a_m_total, 0) + NVL (a_f_total, 0) + NVL (a_o_total, 0) AS a_total,
NVL (a_m_total, 0) AS a_m_total,
NVL (a_f_total, 0) AS a_f_total,
NVL (a_o_total, 0) AS a_o_total,
NVL (b_m_total, 0) + NVL (b_f_total, 0) + NVL (b_o_total, 0) AS b_total,
NVL (b_m_total, 0) AS b_m_total,
NVL (b_f_total, 0) AS b_f_total,
NVL (b_o_total, 0) AS b_o_total,
NVL (c_m_total, 0) + NVL (c_f_total, 0) + NVL (c_o_total, 0) AS c_total,
NVL (c_m_total, 0) AS c_m_total,
NVL (c_f_total, 0) AS c_f_total,
NVL (c_o_total, 0) AS c_o_total,
NVL (d_m_total, 0) + NVL (d_f_total, 0) + NVL (d_o_total, 0) AS d_total,
NVL (d_m_total, 0) AS d_m_total,
NVL (d_f_total, 0) AS d_f_total,
NVL (d_o_total, 0) AS d_o_total
FROM ( SELECT EXTRACT (YEAR FROM date_val) AS year,
CEIL (EXTRACT (MONTH FROM date_val) / 3) AS quarter,
cust_type,
SUM (CASE gender WHEN 'M' THEN 1 ELSE 0 END) AS total_m,
SUM (CASE gender WHEN 'F' THEN 1 ELSE 0 END) AS total_f,
SUM (CASE gender WHEN 'O' THEN 1 ELSE 0 END) AS total_o
FROM cust_table
GROUP BY EXTRACT (YEAR FROM date_val), CEIL (EXTRACT (MONTH FROM date_val) / 3), cust_type)
PIVOT (MAX (total_m) AS m_total, MAX (total_f) AS f_total, MAX (total_o) AS o_total
FOR cust_type
IN ('A' AS a, 'B' AS b, 'C' AS c, 'D' AS d));
Result
YEAR QUARTER QUARTER_TOTAL A_TOTAL A_M_TOTAL A_F_TOTAL A_O_TOTAL B_TOTAL B_M_TOTAL B_F_TOTAL B_O_TOTAL C_TOTAL C_M_TOTAL C_F_TOTAL C_O_TOTAL D_TOTAL D_M_TOTAL D_F_TOTAL D_O_TOTAL
_______ __________ ________________ __________ ____________ ____________ ____________ __________ ____________ ____________ ____________ __________ ____________ ____________ ____________ __________ ____________ ____________ ____________
2018 3 7 1 0 0 1 2 1 0 1 1 0 1 0 3 1 2 0
2018 4 3 2 1 0 0 1 0 1 0 1 0 0 1 0 0 0 0
I used "list" as a source table, then COUNTed distinctly "custid"s for gender category by using PIVOT clause. and assumed that the quarter is "YYYY-Q" formatted. as a last query I summed the counts for each gender for each cust_type to get the result you needed using the pivot table named "cg".
with
list (custid, "date", gender, cust_type) as (
select 25309, to_date('29/10/2018', 'dd/mm/yyyy'), 'M', 'A' from dual union all
select 25310, to_date('09/11/2018', 'dd/mm/yyyy'), 'F', 'B' from dual union all
select 25311, to_date('10/11/2018', 'dd/mm/yyyy'), 'O', 'C' from dual union all
select 25312, to_date('18/09/2018', 'dd/mm/yyyy'), 'F', 'D' from dual union all
select 25313, to_date('18/09/2018', 'dd/mm/yyyy'), 'O', 'A' from dual union all
select 25314, to_date('18/09/2018', 'dd/mm/yyyy'), 'M', 'B' from dual union all
select 25315, to_date('18/09/2018', 'dd/mm/yyyy'), 'F', 'C' from dual union all
select 25316, to_date('18/09/2018', 'dd/mm/yyyy'), 'F', 'D' from dual union all
select 25317, to_date('19/09/2018', 'dd/mm/yyyy'), 'M', 'D' from dual union all
select 25318, to_date('19/09/2018', 'dd/mm/yyyy'), 'O', 'B' from dual
)
,cg as (
select * from (select custid, to_char("date", 'YYYY-Q') as quarter, cust_type, gender from list)
pivot (count(distinct custid) as gender for gender in('F' F, 'M' M, 'O' O))
)
select
quarter,
----------
sum(case when cust_type = 'A' then nvl(f_gender,0)+nvl(m_gender,0)+nvl(o_gender,0) else 0 end) as a_total,
sum(case when cust_type = 'A' then f_gender else 0 end) as a_f,
sum(case when cust_type = 'A' then m_gender else 0 end) as a_m,
sum(case when cust_type = 'A' then o_gender else 0 end) as a_o,
----------
sum(case when cust_type = 'B' then nvl(f_gender,0)+nvl(m_gender,0)+nvl(o_gender,0) else 0 end) as b_total,
sum(case when cust_type = 'B' then f_gender else 0 end) as b_f,
sum(case when cust_type = 'B' then m_gender else 0 end) as b_m,
sum(case when cust_type = 'B' then o_gender else 0 end) as b_o,
----------
sum(case when cust_type = 'C' then nvl(f_gender,0)+nvl(m_gender,0)+nvl(o_gender,0) else 0 end) as c_total,
sum(case when cust_type = 'C' then f_gender else 0 end) as c_f,
sum(case when cust_type = 'C' then m_gender else 0 end) as c_m,
sum(case when cust_type = 'C' then o_gender else 0 end) as c_o,
----------
sum(case when cust_type = 'D' then nvl(f_gender,0)+nvl(m_gender,0)+nvl(o_gender,0) else 0 end) as d_total,
sum(case when cust_type = 'D' then f_gender else 0 end) as d_f,
sum(case when cust_type = 'D' then m_gender else 0 end) as d_m,
sum(case when cust_type = 'D' then o_gender else 0 end) as d_o
from cg
group by quarter;
If your source table name is TOT_POP_DET and have columns "custid", "date", "gender", "cust_type" then you can ignore my data preparation "list" query and start with "cg". I used CTE (common table expression) but using subquery is fine as well. "list" and "cg" are CTEs.
I mean you can start
with cg as (
select * from (select custid, to_char("date", 'YYYY-Q') as quarter, cust_type, gender from TOT_POP_DET)
pivot (count(distinct custid) as gender for gender in('F' F, 'M' M, 'O' O))
)
,...
As you will notice "cg" is distinct count by quarter and cust_type.
PIVOT syntax is as below:
SELECT * FROM (SELECT column1, column2, .. FROM table(s) WHERE condition(s))
PIVOT (aggregate_function(column2) FOR column2 IN ( expr1, expr2, ... expr_n))
ORDER BY expression [ ASC | DESC ];
firstly, selected the needed columns from the source table, then count(distinct custid) as aggregation_function, gender FOR category column, IN (gender list). so got the first result set by quarter and cust_type.
QUARTER CUST_TYPE F_GENDER M_GENDER O_GENDER
2018-3 A 0 0 1
2018-3 B 0 1 1
2018-3 C 1 0 0
2018-3 D 2 1 0
2018-4 A 0 1 0
2018-4 B 1 0 0
2018-4 C 0 0 1
then used this result set, grouped and summed counts with each case to transpose the data to get the final result set.
QUARTER A_TOTAL A_F A_M A_O B_TOTAL B_F B_M B_O C_TOTAL C_F C_M C_O D_TOTAL D_F D_M D_O
2018-4 1 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0
2018-3 1 0 0 1 2 0 1 1 1 1 0 0 3 2 1 0
Additionally, if you change "YYYY-Q" to "YYYY" in PIVOT and execute the query you can get the result by year as below
QUARTER A_TOTAL A_F A_M A_O B_TOTAL B_F B_M B_O C_TOTAL C_F C_M C_O D_TOTAL D_F D_M D_O
2018 2 0 1 1 3 1 1 1 2 1 0 1 3 2 1 0
tried an explanation hope it helps
I hope that the answer could help you. Find it in this fiddle
The version of SQL is Oracle 11g. The cte manipulates your data to produce and produces two columns for your year and quarter. After that, you need to perform a COUNT based on the parameters that you want.
with cte AS(SELECT custid, gender, cust_type, EXTRACT(YEAR FROM cust_date) AS cust_year,
CASE WHEN EXTRACT(MONTH FROM cust_date)<4 THEN 1
WHEN EXTRACT(MONTH FROM cust_date)<7 THEN 2
WHEN EXTRACT(MONTH FROM cust_date)<10 THEN 3
ELSE 4
END AS quarter
FROM your_Table)
SELECT gender, cust_type, cust_year, quarter, COUNT(custid)
FROM cte
GROUP BY gender, cust_type, cust_year, quarter
ORDER BY cust_year, quarter, cust_type
I have the following query which displays sum of employees in a particular group (A1,B1..etc) and in a specifc department.
The problem is when there is no data for a specific department at a specific date then that row with the department name is not displayed at all and I want the query to display all the rows/department names even if they have data in it or not. It should display '0' in such a case.
SELECT NVL(TO_CHAR(COALESCE(dept_name,'NA') ),'TOTAL') AS Department,
SUM (
CASE
WHEN ( emp_group IN('ABC','CDE','EFG','GHI'))
THEN 1
ELSE 0
END) AS A1,
SUM(
CASE
WHEN ( emp_group IN ('XYZ'))
THEN 1
ELSE 0
END) AS B1,
SUM (
CASE
WHEN ( emp_group IN ('ABC','CDE','EFG','GHI','XYZ'))
THEN 1
ELSE 0
END) AS TOTAL
FROM emp e
WHERE
dept_name IN('IT','FI','ACC')
AND e.transaction_date = trunc(sysdate)
GROUP BY rollup(COALESCE(dept_name,'NA'))
Thanks in advance
Start from departments(you should have something like that... ) and do a left join:
SELECT NVL(TO_CHAR(COALESCE(d.dept_name,'NA') ),'TOTAL') AS Department,
SUM (
CASE
WHEN ( emp_group IN('ABC','CDE','EFG','GHI'))
THEN 1
ELSE 0
END) AS A1,
SUM(
CASE
WHEN ( emp_group IN ('XYZ'))
THEN 1
ELSE 0
END) AS B1,
SUM (
CASE
WHEN ( emp_group IN ('ABC','CDE','EFG','GHI','XYZ'))
THEN 1
ELSE 0
END) AS TOTAL
FROM departments d
left join emp e on d.dept_id = e.dept_id
WHERE
d.dept_name IN('IT','FI','ACC')
AND e.transaction_date = trunc(sysdate)
GROUP BY rollup(COALESCE(d.dept_name,'NA'));
If you don't have departments you can left join with dual in this manner:
SELECT NVL(TO_CHAR(COALESCE(e.dept_name,'NA') ),'TOTAL') AS Department,
SUM (
CASE
WHEN ( emp_group IN('ABC','CDE','EFG','GHI'))
THEN 1
ELSE 0
END) AS A1,
SUM(
CASE
WHEN ( emp_group IN ('XYZ'))
THEN 1
ELSE 0
END) AS B1,
SUM (
CASE
WHEN ( emp_group IN ('ABC','CDE','EFG','GHI','XYZ'))
THEN 1
ELSE 0
END) AS TOTAL
FROM (select 'IT' as dept_name from dual union all
select 'FI' from dual union all
select 'ACC' from dual) d
left join emp e on d.dept_name = e.dept_name
WHERE
e.transaction_date = trunc(sysdate)
GROUP BY rollup(COALESCE(e.dept_name,'NA'));
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)