How to group data by different date period in sql? - sql

How to group data by different date period in sql?
For example, I want the data to be grouped from 1/2/2015 to 6/2/2015, 7/2/2015 to 12/2/2015 etc. So far I could only group them according to 1 date range by using the WHERE condition.
SELECT type, count(*)
from table1
WHERE Day(datefield) <=6
Group by type
table1:
type, datefield
typeA, '2015-2-1'
typeB, '2015-2-2'
typeB, '2015-2-9'
typeA, '2015-2-18'
typeB, '2015-2-28'
desired result:
type, no. for day 1-6, no. for day 7-12, no. for day 13-18, no. for day 19-24, no. for day 25-31
type A, 1, 0, 1, 0, 0
type B, 1, 1, 0, 0, 1

First create a derived table where each datefield is replaced by a label ('1-6','7-12',etc.).
Then group that table by its label and type and get the counts. Finally pivot the labels into columns
using conditional aggregation (max(case when ...).
select type,
max(case when date_label = '1-6' then date_count end) '1-6',
max(case when date_label = '7-12' then date_count end) '7-12',
max(case when date_label = '13-18' then date_count end) '13-18',
max(case when date_label = '19-24' then date_count end) '19-24',
max(case when date_label = '25-31' then date_count end) '25-31',
from (
select
type,
date_label,
count(*) date_count
from
(select type,
case when day(datefield) <=6 then '1-6'
when day(datefield) <= 12 then '7-12'
when day(datefield) <= 18 then '13-18'
when day(datefield) <= 24 then '19-24'
when day(datefield) <= 31 then '25-31'
else 'n/a' end as date_label
from table1) t1
group by type, date_label
) t1 group by type

Related

Combine 2 queries together

I am struggling to work out combining a query that should give me 3 columns of Month, total_sold_products and drinks_sold_products
Query 1:
Select month(date), count(id) as total_sold_products
from Products
where date between '2022-01-01' and '2022-12-31'
Query 2
Select month(date), count(id) as drinks_sold_products
from Products where type = 'drinks' and date between '2022-01-01' and '2022-12-31'
I tried the union function but it summed count(id) twice and gave me only 2 columns
Many thanks!
Union is for attaching sets of data on top of each other. You need conditional aggregation or a join. See below.
SELECT MONTH(date),
COUNT(*) AS total_sold_products,
COUNT(CASE WHEN type = 'drinks' THEN 1 ELSE 0 END) AS drinks_sold_products,
FORMAT((CASE
WHEN COUNT(*) > 0 THEN
COUNT(CASE WHEN type = 'drinks' THEN 1 ELSE 0 END)/COUNT(*)
ELSE 0 END),
'P') AS Percentage
FROM Products
WHERE date BETWEEN'2022-01-01' AND '2022-12-31'
GROUP BY MONTH(date)

Count sequence selected columns

I have query below, I want sequence result like the value of 'feb' will sum by jan and feb, value of 'mar' will sum by jan, feb and mar,... . Is there any way to get the result like that?
select A.location as location
, count(Case When SUBSTRING(A.base_date,5,2)='01' Then A.customer_no else null end) as "jan"
, count(Case When SUBSTRING(A.base_date,5,2)='02' Then A.customer_no else null end) as "feb"
....
, count(Case When SUBSTRING(A.base_date,5,2)='12' Then A.customer_no else null end) as "dec"
from table_income A group by A.location;
SQL is a much more effective language when you think in rows rather than columns (normalisation).
For example, having one row per month is much simpler...
SELECT
location,
SUBSTRING(base_date,5,2) AS base_month,
SUM(COUNT(customer_no))
OVER (
PARTITION BY location
ORDER BY SUBSTRING(base_date,5,2)
)
AS count_cust
FROM
table_income
GROUP BY
location,
SUBSTRING(base_date,5,2)
Side notes:
If your base_date is a string, it shouldn't be, use data-types relevant to the data
If your base_date is a date or timestamp, you should really use date/timestamp functions, such as EXTRACT(month FROM base_date).
You probably should also account for different years...
SELECT
location,
DATE_TRUNC('month', base_date) AS base_month,
SUM(COUNT(customer_no))
OVER (
PARTITION BY location, DATE_TRUNC('year', base_date)
ORDER BY DATE_TRUNC('month', base_date)
)
AS count_cust
FROM
table_income
GROUP BY
location,
DATE_TRUNC('month', base_date)
Try this :
SELECT A.location as location
, count(Case When SUBSTRING(A.base_date,5,2) in ('01') Then A.customer_no else null end) as "jan"
, count(Case When SUBSTRING(A.base_date,5,2) in ('01','02') Then A.customer_no else null end) as "feb"
....
, count(Case When SUBSTRING(A.base_date,5,2) in ('01','02',...'12') Then A.customer_no else null end) as "dec"
from table_income A group by A.location;

MSSQL Group by and Select rows from grouping

I'm trying to figure out if what I'm trying to do is possible. Instead of resorting to multiple queries on a table, I wanted to group the records by business date and id then group by the id and select one date for a field and another date for the other field.
SELECT
*
{AMOUNT FROM DATE}
{AMOUNT FROM OTHER DATE}
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
AS subquery
GROUP BY id
It seems that you're looking to do a pivot query. I usually use cross tabs for this. Based on the query you posted, it could look like:
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)AS subquery
GROUP BY id;
You could also use a CTE.
WITH CTE AS(
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
Or even be a rebel and do the operation directly.
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
However, some people have tested for performance and found that pre-aggregating can improve performance.
If I understand you correctly, then you're just trying to pivot, but only with two particular dates:
select id,
date1 = sum(iif(date = '2000-01-01', amount, null)),
date2 = sum(iif(date = '2000-01-02', amount, null))
from [table]
group by id

Bring through previous 12 months count while grouping by period

I'm trying to use the below code to bring though a count of all lines in the last 12 month period for the period and plant, please see the output below.
So for example with the below output, rather than the 12 months column currently containing the total for the period, I want the count using a period between 201001-201101 (Please note, my example was only for the dataset below and the 12 months column needs to adapt for each period).
Period Plant Stock Special MonthTotal 12Months
201101 0EA0 27 0 27 27
201101 0EB0 35 2 37 37
The issue I'm having is that rather than bring through the last 12 month count, my code is merely bringing through the count for the current period. Please can someone assist?
select
convert(varchar(6),dateadd(mm,0,P.Dt),112) as Period,P.Plant,
Sum(Case When Left(Upper(Material),2) = 'ZZ' then 1 else 0 end) as Stock,
Sum(Case When Left(Upper(Material),2) <> 'ZZ' then 1 else 0 end) as Special
,Count(*) as MonthTotal,Sum(Case When
convert(varchar(6),dateadd(mm,0,P.Dt),112)
Between
convert(varchar(6),dateadd(mm,-12,P.Dt),112)
And
convert(varchar(6),dateadd(mm,0,P.Dt),112) Then 1 else 0 End
)as [12Months]
from
iesaonline.dbo.DS_POs as P where
Plant IN(
Select Client From METRICS.DBO.CO_001_Plants_090_Final
where CustGrp = 'Hovis'
)
Group by
P.Plant,convert(varchar(6),dateadd(mm,0,P.Dt),112)
order by
convert(varchar(6),dateadd(mm,0,Dt),112),Plant
The problem seems to be that you're grouping by year/month and trying to sum values outside that year/month range. Without sample data, I can't be certain, but it sounds like you want a rolling 12 month sum. Something like below should get you where you want to go.
;with monthlySubtotal as
(
select
dateadd(m, 1-datepart(day, p.dt), p.dt) as PeriodMonth
,P.Plant
,Sum(Case When Left(Upper(Material),2) = 'ZZ' then 1 else 0 end) as Stock
,Sum(Case When Left(Upper(Material),2) <> 'ZZ' then 1 else 0 end) as Special
,Count(*) as MonthTotal
from
iesaonline.dbo.DS_POs as P where
Plant IN(
Select Client From METRICS.DBO.CO_001_Plants_090_Final
where CustGrp = 'Hovis'
)
Group by
P.Plant
,dateadd(m, 1-datepart(day, p.dt), p.dt)
)
SELECT
convert(varchar(6),m1.PeriodMonth,112) Period
, m1.Plant
, m1.Stock
, m1.Special
, m1.MonthTotal
, SUM(m2.monthtotal) 12mototal
FROM monthlySubtotal m1
JOIN monthlySubtotal m2
ON m2.plant = m1.plant
AND m2.periodmonth BETWEEN dateadd(m, -11, m1.periodmonth)
AND m1.periodmonth
--You may want to filter this
--WHERE m1.periodmonth >= startdate
GROUP BY
convert(varchar(6),m1.PeriodMonth,112)
, m1.Plant
, m1.Stock
, m1.Special
, m1.MonthTotal
ORDER BY
Period
, Plant
There is no need to do everything at the same time.
It's easier to first get the monthly values
SELECT DATEADD(month, DATEDIFF(month, 0, Dt), 0) as FOM
, Plant
, Stock = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 1 ELSE 0 END)
, Special = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 0 ELSE 1 END)
FROM DS_POs
GROUP BY Plant, DATEADD(month, DATEDIFF(month, 0, Dt), 0)
and using that as the base to get the last 12 month of result using a CROSS APPLY
WITH DS_POSM AS (
SELECT DATEADD(month, DATEDIFF(month, 0, Dt), 0) as FOM
, Plant
, Stock = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 1 ELSE 0 END)
, Special = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 0 ELSE 1 END)
FROM DS_POs
GROUP BY Plant, DATEADD(month, DATEDIFF(month, 0, Dt), 0)
)
SELECT Convert(char(6), FOM, 112) Period
, Plant
, Stock
, Special
, MonthTotal = Stock + Special
, ly.[12Months]
FROM DS_POSM a
CROSS APPLY (SELECT Sum(Stock + Special) [12Months]
FROM DS_POSM lastyear
WHERE lastyear.FOM Between DateAdd(mm, -12, a.FOM) And a.FOM
AND lastyear.Plant = a.Plant
) ly
ORDER BY FOM, Plant
DATEADD(month, DATEDIFF(month, 0, Dt), 0) get the first day of the month of Dt

counting events over flexible ranges

I am trying to count events (which are rows in the event_table) in the year before and the year after a particular target date for each person. For example, say I have a person 100 and target date is 10/01/2012. I would like to count events in 9/30/2011-9/30/2012 and in 10/02/2012-9/30/2013.
My query looks like:
select *
from (
select id, target_date
from subsample_table
) as i
left join (
select id, event_date, count(*) as N
, case when event_date between target_date-365 and target_date-1 then 0
when event_date between target_date+1 and target_date+365 then 1
else 2 end as after
from event_table
group by id, target_date, period
) as h
on i.id = h.id
and i.target_date = h.event_date
The output should look something like:
id target_date after N
100 10/01/2012 0 1000
100 10/01/2012 1 0
It's possible that some people do not have any events in the before or after periods (or both), and it would be nice to have zeros in that case. I don't care about the events outside the 730 days.
Any suggestions would be greatly appreciated.
I think the following may approach what you are trying to accomplish.
select id
, target_date
, event_date
, count(*) as N
, SUM(case when event_date between target_date-365 and target_date-1
then 1
else 0
end) AS Prior_
, SUM(case when event_date between target_date+1 and target_date+365
then 1
else 0
end) as After_
from subsample_table i
left join
event_table h
on i.id = h.id
and i.target_date = h.event_date
group by id, target_date, period
This is a generic answer. I don't know what date functions teradata has, so I will use sql server syntax.
select id, target_date, sum(before) before, sum(after) after, sum(righton) righton
from yourtable t
join (
select id, target_date td
, case when yourdate >= dateadd(year, -1, target_date)
and yourdate < target_date then 1 else 0 end before
, case when yourdate <= dateadd(year, 1, target_date)
and yourdate > target_date then 1 else 0 end after
, case when yourdate = target_date then 1 else 0 end righton
from yourtable
where whatever
group by id, target_date) sq on t.id = sq.id and target_date = dt
where whatever
group by id, target_date
This answer assumes that an id can have more than one target date.