SQL query to group by age range from date created - sql

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

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 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 column count

[![enter image description here][1]][1]
I need to expected result from the attached screenshot
month empid emp_name p count wo count Totalhrs
----------------------------------------------------------------------------
FEB 00113 HUda salem al kaabi 25 4 1250
You can try a query like this :
Select
empid
max(emp_name) as emp_name,
count(*) as count,
sum(case when dstatus='wo' then 1 else 0 end) as wo_count,
sum(total_hrs) as totalhrs
from
<your_table>
where
p_date like '2020-10%'
group by
empid
Use conditional aggregation:
select empid, emp_name,
sum(case when dstatus = 'P' then 1 else 0 end) p_count,
sum(case when dstatus = 'WO' then 1 else 0 end) wo_count,
sum(total_hrs) total_hrs
from mytable
group by empid, emp_name
SELECT empid, emp_name, COUNT(*) 'p count', COUNT( IIF(DSTATUS = 'WO',1,0 )) 'wo count', COUNT(Total_Hrs) 'Total Hours'
FROM tb
GROUP BY empid, emp_name, p_count, wo_count, Total_Hrs

I am looking to find customers repurchase frequency in SQL from their first purchase date

I am trying to find the customer's repurchase rates from their first order date. For example, for 2016, how many customer purchased 1X in days 1-365 from their initial purchase, how many purchased twice etc.
I have a transaction_detail table which looks like below:
txn_date Customer_ID Transaction_Number Sales
1/2/2019 1 12345 $10
4/3/2018 1 65890 $20
3/22/2019 3 64453 $30
4/3/2019 4 88567 $20
5/21/2019 4 85446 $15
1/23/2018 5 89464 $40
4/3/2019 5 99674 $30
4/3/2019 6 32224 $20
1/23/2018 6 46466 $30
1/20/2018 7 56558 $30
I am able to find the customers who have shopped in 2016 and how many times have they repurchased in 2016, but I need to find the customer who have shopped in 2016 and how many times have they come back from their first purchase date.
I need a starting point for the query, I am not sure how to build this logic in my SQL code.
Any help would be appreciated.
I am using the below query:
WITH by_year
AS (SELECT
Customer_ID,
to_char(txn_date, 'YYYY') AS visit_year
FROM table
GROUP BY Customer_ID, to_char(txn_date, 'YYYY')),
with_first_year
AS (SELECT
Customer_ID,
visit_year,
FIRST_VALUE(visit_year) OVER (PARTITION BY Customer_ID ORDER BY visit_year) AS first_year
FROM by_year),
with_year_number
AS (SELECT
Customer_ID,
visit_year,
first_year,
(visit_year - first_year) AS year_number
FROM with_first_year)
SELECT
first_year AS first_year,
SUM(CASE WHEN year_number = 0 THEN 1 ELSE 0 END) AS year_0,
SUM(CASE WHEN year_number = 1 THEN 1 ELSE 0 END) AS year_1,
SUM(CASE WHEN year_number = 2 THEN 1 ELSE 0 END) AS year_2,
SUM(CASE WHEN year_number = 3 THEN 1 ELSE 0 END) AS year_3,
SUM(CASE WHEN year_number = 4 THEN 1 ELSE 0 END) AS year_4,
SUM(CASE WHEN year_number = 5 THEN 1 ELSE 0 END) AS year_5,
SUM(CASE WHEN year_number = 6 THEN 1 ELSE 0 END) AS year_6,
SUM(CASE WHEN year_number = 7 THEN 1 ELSE 0 END) AS year_7,
SUM(CASE WHEN year_number = 8 THEN 1 ELSE 0 END) AS year_8,
SUM(CASE WHEN year_number = 9 THEN 1 ELSE 0 END) AS year_9
FROM with_year_number
GROUP BY first_year
ORDER BY first_year
Use window functions and aggregation:
select cnt, count(*), min(customer_id), max(customer_id)
from (select customer_id, count(*) as cnt
from (select td.*,
min(txn_date) over (partition by Customer_ID) as min_txn_date
from transaction_detail td
) td
where txn_date >= min_txn_date and txn_date < min_txn_date + interval '365' day
group by customer_id
) c
group by cnt
order by cnt;
So as per my understanding, you want to know the count of the distinct person who first purchased in 2016 and repurchased after one year or more from date of purchase.
Select * from
(
Select customer_id,
Floor(months_between(txn_date, lead_txn_date)/12) as num_years
From
(
Select customer_id,
txn_date,
row_number() over (partition by Customer_ID order by txn_date) as rn,
lead(txn_date) over (partition by Customer_ID order by txn_date) as lead_txn_date
From your_table
)
Where txn_date >= date '2016-01-01'
and txn_date < date '2017-01-01'
and rn = 1
And months_between(txn_date, lead_txn_date) >= 12
)
Pivot
(
Count(1) for num_year in (1,2,3,4)
)
Ultimately, we are finding the number of years between first and second purchase of the customer. And first purchase must be in 2016.
Cheers!!

infuse a sum of the value in the another column with a different filter than the total count column

First here's a sample table.
enter image description here
Provider_name patient date status length
AF AGUIR00001 07/05/2018 3 30
AF ABBOT00001 07/05/2018 30
BB ADAMS00001 07/05/2018 3 30
BB ACEVE00001 07/06/2018 3 30
I have created a query that lets me count the total number of appointments versus the number of appointments with a certain status(eg checked out). I was able to create it and group it by provider.
select provider_name,
count(patient) total,
sum(case when status = 3 then 1 else 0 end) as Checkedout
from appointment
group by provider_name
Then I moved on to the next phase which was to get the total length of those appointments with checkedout status. I made this query but it does not break down into each provider.
select provider_name,
count(patient) total,
sum(case when status = 3 then 1 else 0 end) as Checkedout,
(select sum(length) from appointment where status = 3
and date between '06/01/2018' and '07/06/2018')
from appointment where date between '06/01/2018' and '07/06/2018'
group by provider_name
I need it so that the last column in the query is segregated per provider_name.
Thank you in advance for helping me out.
Actually, you were on the right way, try this:
select provider_name,
count(patient) total,
sum(case when status = 3 then 1 else 0 end) as Checkedout,
sum(case when status = 3 then length else 0 end) as len_status3
from appointment
where date between '2018-01-06' and '2018-06-07'
group by provider_name;
According to your last comment, you need a WITH ROLLUP modifier for GROUP BY as in the following :
select coalesce(provider_name,'Total') as provider_name,
count(patient) total,
sum(case when status = 3 then 1 else 0 end) as Checkedout,
sum(case when status = 3 then length else 0 end) as len_status3
from appointment
where date between '2018-01-06' and '2018-06-07'
group by provider_name with rollup;
SQL Fiddle Demo
you shoul do as for checkedoutout
select provider_name,
count(patient) total,
sum(case when status = 3 then 1 else 0 end) as Checkedout,
sum( case when status = 3 then length else 0 ) as total_length
from appointment where date between '06/01/2018' and '07/06/2018'
group by provider_name