Aggregating a column based on date dynamically in SQL - sql

This is more of a conceptual question. I'm trying to compare the sales that have happened this year with the last year. But the comparison should be dynamic, as in, for this year I consider the sales that have happened till yesterday, and I need to consider the sales for the previous year till the same date last year as well.
For example, today is Dec 24th, so for 2021 I aggregate the sales till Dec 23rd, and I need to do the same for 2020 as well i.e. till Dec 23rd of 2020. And for tomorrow's report the aggregate should be till Dec 24th of 2021 and Dec 24th of 2020 respectively.
My code so far:
SELECT product_category,
SUM(CASE WHEN purchase_date >= '2021-01-01' AND purchase_date < CURRENT_DATE THEN sales_revenue ELSE 0 END) AS revenue_2021,
SUM(CASE WHEN purchase_date >= '2020-01-01' AND purchase_date < '2021-01-01' THEN sales_revenue ELSE 0 END) AS revenue_2020
FROM sales_table
GROUP BY 1
ORDER BY 1
Here, for 2021, my code works. But for 2020 it would give the whole year (2020's) sum. Is there anyway I can make this dynamic for 2020 just the same way it happens for 2021?

SELECT product_category,
SUM(CASE WHEN purchase_date >= DATEADD(yy, DATEDIFF(yy, 0, CURRENT_DATE), 0)
AND purchase_date < CURRENT_DATE THEN sales_revenue ELSE 0 END) AS revenue_2021,
SUM(CASE WHEN purchase_date >= DATEADD(yy, DATEDIFF(yy, 0, CURRENT_DATE)-1, 0) AND purchase_date < dateadd(yy, -1, CURRENT_DATE) THEN sales_revenue ELSE 0 END) AS revenue_2020
FROM sales_table
WHERE
purchase_date >= DATEADD(yy, DATEDIFF(yy, 0, CURRENT_DATE)-1, 0)
GROUP BY 1
ORDER BY 1

I figured this out by myself and this seems to work.
SELECT EXTRACT(YEAR FROM DATE_TRUNC('YEAR',purchase_date) AS Year,
product_category,
SUM(sales_revenue)
FROM sales_table
WHERE DATE_PART('MONTH',purchase_date)*100 + DATE_PART('DAY',purchase_date_time)
< DATE_PART('MONTH',CURRENT_DATE)*100 + DATE_PART('DAY',CURRENT_DATE)
GROUP BY 1,2
ORDER BY 1,2

Related

Summing sales dollars for most recent month and 2nd most recent month

For each of the 12 months, I'm looking to create a field that sums the sales dollars at the account level for the most recent month and the 2nd most recent month based on the current date.
For example, given that today's date is 10/6/22, 'MostRecentNovember' would sum up sales from November 2021. '2ndMostRecentNovember' would sum up sales from November 2020. Once the current date moves into November 2022, this query would adjust to pull MostRecentNovember sales from 2022 and 2ndMostRecentNovember sales from 2021.
Conversely, given that today's date is 10/6/22 'MostRecentJune' would sum up sales from June 2022 and '2ndMostRecentJune' would sum up sales from June 2021.
Below is my attempt at this code, I think this gets partially there, but not sure it's exactly what I want
SELECT NovemberMostRecent_Value =
sum(case when datepart(year,tran_date) = datepart(year, getdate())
AND DATEPART(month, tran_date) = 11 then value else 0 end)
NovemberSecondMostRecent_Value =
sum(case when datepart(year,tran_date) = datepart(year, getdate())-1
AND DATEPART(month, tran_date) = 11 then value else 0 end)
Here's a snippet of the source data table
account_no
tran_date
value
123
11/22/21
500
123
11/1/21
500
123
11/20/20
1500
123
6/3/22
5000
123
6/4/21
2000
456
11/3/20
525
456
11/4/21
125
Per Request in Comments. A table of desired Results
account_no
NovemberMostRecent
November2ndMostRecent
June MostRecent
June2ndMostRecent
123
1000
1500
5000
2000
456
125
525
0
0
Why don't you just sum up the sales then group by month and year for the last two years? Wouldn't that solve the problem?
Or you can show a table that depicts what you are trying to achieve.
This should work fine.
Note: I only assume the account_no is the same for all the rows, if they are different, then you will need to pass it as a condition in the subquery.
WITH CTE AS
(SELECT (SELECT SUM(value) FROM tablename WHERE datepart(year, tran_date) = YEAR(getdate()) AND datepart(month, tran_date) = 11)
AS first_value,
(SELECT SUM(value) FROM tablename WHERE datepart(year, tran_date) = YEAR(getdate())-1 AND datepart(month, tran_date) = 11)
AS second_value,
(SELECT SUM(value) FROM tablename WHERE datepart(year, tran_date) = YEAR(getdate())-2 AND datepart(month, tran_date) = 11)
AS third_value)
SELECT IIF (first_value>0, first_value, second_value) AS NovemberMostRecent_Value,
IIF (first_value>0, second_value, third_value) AS NovemberSecondMostRecent_Value FROM CTE;

Year over Year by Month Comparison and Month to Date in BigQuery

Edit: #shawnt00 has the correct answer. Thank you very much!
I am having trouble accurately doing a year over year comparison by month but at any point during the month. For example for August 2022 vs 2021, I want to compare August 1 - August 25, rather than full month of August 2021.
I am also using a daily date field.
I want the final result to basically be:
Product_ID, Year, Month, PY_Sales, CY_Sales
Edit: I have daily totals. Some products do have not sales on certain days though:
product_id
sale_date
units
1
2021-01-01
5
2
2021-01-02
4
...
...
...
1
2021-06-05
2
2
2022-01-06
1
2
2022-08-15
9
This is the code I have, but it doesn't do MTD. So 2021 August is the entire month of August and I want it the same dates for 2022. I used this code because some products do not have sales on certain months.
WITH cte AS
(
SELECT
PRODUCT_ID,
EXTRACT(YEAR FROM SALE_DATE) AS Year,
EXTRACT(MONTH FROM SALE_DATE) AS Month,
CONCAT(EXTRACT(YEAR FROM SALE_DATE), '-',EXTRACT(MONTH FROM SALE_DATE)) AS Year_Month,
SUM(Units) AS Units
FROM data
WHERE Product_ID = 1
AND DATE(SALE_DATE) >= '2019-01-01'
GROUP BY 1, 2, 3
),
diff AS
(
SELECT
COALESCE(c.PRODUCT_ID, p.PRODUCT_ID) AS Product_ID,
COALESCE(c.Year, p.Year + 1) AS Year,
COALESCE(c.Month, p.Month) AS Month,
IFNULL(c.Units, 0) AS Current_Units,
IFNULL(p.Units, 0) AS Previous_Units,
NULLIF(((IFNULL(c.Units, 0) - IFNULL(p.Units,0)) / p.Units),0) * 100 AS Percent_Change
FROM CTE c
FULL OUTER JOIN CTE p ON c.PRODUCT_ID = p.PRODUCT_ID AND c.Year = p.Year + 1 AND c.Month = p.Month
WHERE c.Year <= EXTRACT(YEAR FROM CURRENT_DATE())
ORDER BY 2, c.Year, c.Month
)
SELECT *
FROM diff
--This is to avoid dividing by 0
WHERE diff.Previous_Units > 0
--AND Percent_Change <= -.5
You could just roll up two different monthly totals and then switch for the current month comparison:
with agg as (
select
PRODUCT_ID,
extract(year from SALE_DATE) as yr,
extract(month from SALE_DATE) as mth,
sum(Units) as Units,
sum(case when extract(day from SALE_DATE) <= extract(day from current_date())
then Units end) as UnitsMTD
from data
where date(SALE_DATE) >= '2019-01-01' -- one year before report output
group by 1, 2, 3
)
select c.Yr, c.Mth, c.PRODUCT_ID,
case when Yr = extract(year from current_date())
and Mth = extract(month from current_date())
then (c.UnitsMTD - p.UnitsMTD) / p.UnitsMTD
else (c.Units - p.Units ) / p.Units
end as Percent_Change
from agg c left outer join agg p
on p.Product_ID = c.Product_ID and p.Yr = c.Yr - 1 and p.Mth = c.Mth
order by c.Yr, c.Mth, c.PRODUCT_ID;
Note my earlier comment about leap years. This will treat February 28 of the year following a leap year as an "MTD" month. You might need to handle that differently inside the case expression.

Get the COUNT of data for last 4 years monthly by using CASE statement

I tried this:
SELECT DISTINCT
MONTH(ENCOUNTER_DATE) 'MONTH',
COUNT(CASE WHEN ENCOUNTER_DATE >= DATEADD(YEAR, -1, GETDATE()) THEN CUSTOMER_ID END) ,
COUNT(CASE WHEN ENCOUNTER_DATE >= DATEADD(YEAR, -2, GETDATE()) THEN CUSTOMER_ID END),
COUNT(CASE WHEN ENCOUNTER_DATE >= DATEADD(YEAR, -3, GETDATE()) THEN CUSTOMER_ID END),
COUNT(CASE WHEN ENCOUNTER_DATE >= DATEADD(YEAR, -4, GETDATE()) THEN CUSTOMER_ID END)
FROM
PATIENT_ENCOUNTERS
GROUP BY
MONTH(ENCOUNTER_DATE)
ORDER BY
MONTH(ENCOUNTER_DATE)
The count of every month data is getting added to the next year of the same month.
I cross checked with the below query where the data is not matching
SELECT
YEAR(ENCOUNTER_DATE), COUNT(CUSTOMER_ID)
FROM
PATIENT_ENCOUNTERS
WHERE
ENCOUNTER_DATE >= DATEADD(YEAR, -4, GETDATE())
GROUP BY
YEAR(ENCOUNTER_DATE)
ORDER BY
YEAR(ENCOUNTER_DATE) DESC
Can someone help me to find the data monthly in last 4 years by using case statement only.
Your problem is that you're setting a begin date but not an end date. Try something similar to the following.
COUNT(CASE WHEN ENCOUNTER_DATE >= DATEADD(YEAR, -4, GETDATE()) AND ENCOUNTER_DATE < DATEADD(YEAR, -3, GETDATE()) THEN CUSTOMER_ID END)
Try this:
SELECT DISTINCT MONTH(ENCOUNTER_DATE) 'MONTH',
COUNT(CASE WHEN year(ENCOUNTER_DATE) = YEAR(GETDATE()) THEN CUSTOMER_ID END) ,
COUNT(CASE WHEN year(ENCOUNTER_DATE)= YEAR(GETDATE())-1 THEN CUSTOMER_ID END),
COUNT(CASE WHEN year(ENCOUNTER_DATE)= YEAR(GETDATE())-2 THEN CUSTOMER_ID END),
COUNT(CASE WHEN year(ENCOUNTER_DATE)= YEAR(GETDATE())-3 THEN CUSTOMER_ID END)
FROM PATIENT_ENCOUNTERS
GROUP BY MONTH(ENCOUNTER_DATE)
ORDER BY MONTH(ENCOUNTER_DATE)
Here with YEAR(GETDATE()) I got the year of current year and with YEAR(GETDATE())-1 year part of last year and so on. Then I have compared this year with year of encounter_date as year(encounter_date) = YEAR(GETDATE()) to get the customer_id of that year which are grouped by later by month.
To get the year wise cumulative count:
SELECT DISTINCT MONTH(ENCOUNTER_DATE) 'MONTH',
COUNT(CASE WHEN year(ENCOUNTER_DATE) = YEAR(GETDATE()) THEN CUSTOMER_ID END) ,
COUNT(CASE WHEN year(ENCOUNTER_DATE)>= YEAR(GETDATE())-1 THEN CUSTOMER_ID END),
COUNT(CASE WHEN year(ENCOUNTER_DATE)>= YEAR(GETDATE())-2 THEN CUSTOMER_ID END),
COUNT(CASE WHEN year(ENCOUNTER_DATE)>= YEAR(GETDATE())-3 THEN CUSTOMER_ID END)
FROM PATIENT_ENCOUNTERS
GROUP BY MONTH(ENCOUNTER_DATE)
ORDER BY MONTH(ENCOUNTER_DATE)

Split number to ranges in SQL

I am trying to write a SQL query that show the distribution of customers seniority since 2015/01/31:
Up to one month
Between one and six months
Between six months and one year
Over a year
I succeeded to split and group the number of months of the customers.
SELECT Seniority, COUNT(Customer_ID) [Number of Customers]
FROM
(SELECT Customer_ID,
DATEDIFF(MONTH, MIN(CONVERT(datetime, Order_Date)), '2015/01/31') Seniority
FROM Orders
GROUP BY Customer_ID) t
GROUP BY Seniority
How can I split by given ranges?
Expected:
Seniorty | Number of Customers
Up to one month | 0
Between one and six months | 14
Between six months and one year | 1
Over a year | 0
Use conditional aggregation:
WITH cte AS (
SELECT
'Up to one month' AS Seniority,
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') < 1 THEN 1 END) AS [Number of Customers],
1 AS position
FROM Orders
UNION ALL
SELECT
'Between one and six months',
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') >= 1 AND
DATEDIFF(MONTH, Order_Date, '2015-01-31') < 6 THEN 1 END),
2
FROM Orders
UNION ALL
SELECT
'Between six months and one year',
COUNT( CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') >= 6 AND
DATEDIFF(MONTH, Order_Date, '2015-01-31') < 12 THEN 1 END),
3
FROM Orders
UNION ALL
SELECT
'Over a year',
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') > 12 THEN 1 END),
4
FROM Orders
)
SELECT
Seniority,
[Number of Customers]
FROM cte
ORDER BY
position;
This answer assumes that the Order_Date column is already date or datetime. If not, then the first thing you should do is to convert this column to an actual date type.
WITH CTE AS (
SELECT CUSTOMER_ID,
CASE WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE)=1 THEN 'Up to one month'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE) BETWEEN 1 AND 6 THEN 'Between one and six months'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE) BETWEEN 6 AND 12 THEN 'Between six months and one year'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE)>12 THEN 'Over a year'
END AS SENIORITY
FROM ORDERS
)
SELECT SENIORITY AS 'Seniority', COUNT(CUSTOMER_ID) AS 'Number of Customers'
FROM CTE
WHERE SENIORITY IS NOT NULL
GROUP BY SENIORITY

SQL Count Entries for each Month of the last 6 Months

I got a problem while trying to count the entries that were created in a month for the last 6 months.
The table looks like this:
A B C D
Year Month Startingdate Identifier
-----------------------------------------
2019 3 2019-03-12 OAM_1903121
2019 2 2019-03-21 OAM_1902211
And the result should look like:
A B C
Year Month Amount of orders
---------------------------------
2019 3 26
2019 2 34
This is what I have so far, but it doesn't get me the proper results:
SELECT year, month, COUNT(Startingdate) as Amount
FROM table
WHERE Startingdate > ((TRUNC(add_months(sysdate,-3) , 'MM'))-1)
GROUP BY year, month
I have not tested it, but it should work:
select year, month, count(Stringdate) as Amount_of_order
from table
where Stringdate between add_months(sysdate, -6) and sysdate
group by year, month;
Let me know.
Try that :
SELECT YEAR(Startingdate) AS [Year], MONTH(Startingdate) AS [Month], COUNT(*) AS Amount
FROM table
WHERE Startingdate > DATEADD(MONTH, -6, GETDATE())
GROUP BY YEAR(Startingdate), MONTH(Startingdate)
ORDER BY YEAR(Startingdate), MONTH(Startingdate) DESC
I think your issue is the filtering. If so, this should handle the most recent six full months:
SELECT year, month, COUNT(*) as num_orders
FROM table
WHERE Startingdate >= TRUNC(add_months(sysdate, -6) , 'MM')
GROUP BY year, month;