SQL cumulative previous 12 months - breakdown per month - mysql

Can anyone assist in a sql statement I am trying to write? I'm using SSRS r1 (either sql or ssrs solution is fine)
How do I:
show count measure split by month and year
for each of those months, I want to count the previous cumulative 12 months
e.g.
2012 jan: counts feb 2011 - jan 2012
2012 feb: counts mar 2011 - feb 2012
2012 mar: counts apr 2011 - mar 2012
I have started this code but it's incorrect, however it gives you an idea of what I am trying to achieve (this issue is I have to calc month and year from a date)
select
count(a.measure) count
,month(a.StartDate)
,year(a.StartDate)
from
a
where
a.StartDate >= DATEADD(mm,DATEDIFF(mm,0,#datepromt)-12,0) as startdateYrAgo --1st month 1 year ago 01/01/2012
and a.StartDate <= DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#datepromt)+1,0)) as startdateEOM --last day of month 31/01/2013
group by
month(a.StartDate)
,year(a.StartDate)

Here you have an idea for the query, it have to look something like below;
SELECT
periods.year
,periods.month
,measures.cnt
FROM (
SELECT DISTINCT
year = YEAR(StartDate)
, month = MONTH(StartDate)
, month_running = DATEDIFF(mm, 0, StartDate)
FROM a
GROUP BY YEAR(StartDate), MONTH(StartDate), DATEDIFF(mm, 0, StartDate)
) periods
JOIN (
SELECT month_running = DATEDIFF(mm, 0, StartDate), cnt = COUNT(measure)
FROM a
GROUP BY DATEDIFF(mm, 0, StartDate)
) measures
ON measures.month_running BETWEEN periods.month_running - 12 AND periods.month_running - 1

Related

Extracting year and total days between range of date - SQL

I have data with start date and end date (Say 20th Feb 2018 to 20th Feb 2020), I want to find out the total days in every year inside this range.
For example:
2018 - x days
, 2019 - 365 days
, 2020 - y days etc.
Is there a way I can do in SQL without hardcoding year values?
I tried hardcoding the values and it worked well. But I want a solution without hardcoding year values
I'm not familiar enough with MySql to know if this will port, however here is a tested and confirmed SQL Server solution.
The fiddle link is here for your use.
Given start dates 02/20/2018 and 02/20/2020, the result set is as follows:
Year
periodStart
periodEnd
DaysInPeriod
2018
2018-02-20
2018-12-31
314
2019
2019-01-01
2019-12-31
365
2020
2020-01-01
2020-02-20
51
Declare #StartDate date = '2018-02-20', #EndDate date = '2020-02-20';
WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n)),
Years AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS Year
FROM x ones, x tens, x hundreds, x thousands)
SELECT Years.Year,
CASE
WHEN Year(#StartDate) = Years.year THEN #StartDate
ELSE DATEFROMPARTS(years.year, 01, 01)
END AS periodStart,
CASE
WHEN Year(#EndDate) = Years.year THEN #EndDate
ELSE DATEFROMPARTS(years.year, 12, 31)
END AS periodEnd,
DATEDIFF(day,
CASE
WHEN Year(#StartDate) = Years.year THEN #StartDate
ELSE DATEFROMPARTS(years.year, 01, 01)
END,
CASE
WHEN Year(#EndDate) = Years.year THEN #EndDate
ELSE DATEFROMPARTS(years.year, 12, 31)
END
) + 1 AS DaysInPeriod
FROM Years
WHERE Years.Year >= Year(#StartDate)
AND Years.Year <= Year(#EndDate)
Using WITH RECURSIVE to create range of dates then we can easly count the number of days for each year using DATEDIFF
WITH RECURSIVE dates AS
(
SELECT min(start_date) as start_date, DATE_FORMAT(min(start_date),'%Y-12-31') as last_day FROM mytable
UNION ALL
SELECT DATE_FORMAT(start_date + INTERVAL 1 YEAR,'%Y-01-01'),
DATE_FORMAT(start_date + INTERVAL 1 YEAR,'%Y-12-31')
FROM dates
WHERE DATE_FORMAT(start_date + INTERVAL 1 YEAR,'%Y-01-01') <= (SELECT MAX(end_date) FROM mytable)
),
cte2 as (
SELECT d.start_date as start_day, if(YEAR(d.start_date) = YEAR(m.end_date), m.end_date, d.last_day) as last_day
FROM dates d, mytable m
)
select *, DATEDIFF(last_day, start_day)+1 as total_days
from cte2;
Demo here
You are looking for the DATEDIFF function.
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_datediff
DATEDIFF() returns expr1 − expr2 expressed as a value in days from one date to the other. expr1 and expr2 are date or date-and-time expressions.
You are free to specify e.g. "2019-01-01" or "2020-01-01"
as input arguments to DATEDIFF.
You may find it convenient to store several January 1st
dates in a calendar reporting table, if you want SELECT to loop
over several years and report on number of days in each year.

Windows Max() function in SQL fetches incorrect max values

I am using the max() windows function to get the maximum money raised for each year considering there are three categories of campaign length. I need to see which campaign length i.e '<=30 days', '36 - 60 days' and '>60 days' is able to raise more money year on year.
I wrote
select
year,
campaign_length_category,
money_raised,
max(money_raised) over (partition by year ) as max_money_raised
FROM
(
SELECT
year,
campaign_length_category,
concat('$', format(sum(pledged),2,'en_US')) as money_raised
FROM
(
select
extract(year from launched) Year,
case
when campaign_length >= 1 and campaign_length <= 30 then '<=30 days'
when campaign_length > 35 and campaign_length <= 60 then '36 - 60 days'
else '>60 days'
end as campaign_length_category,
pledged
from
(
select
launched,
outcome,
datediff(cast(deadline as date),cast(launched as date)) campaign_length,
pledged
from
campaign
) t1
)t2
group by 1, 2
)t3
order by 1
However, it's not showing the maximum value for each year in the output. For the year 2011 and 2012 it fetches wrong values.
year
campaign_length_category
money_raised
max_money_raised
2009
<=30 days
$25,852.12
$64,088.48
2009
>60 days
$64,088.48
$64,088.48
2009
36 - 60 days
$31,978.64
$64,088.48
2010
<=30 days
$201,063.08
$467,862.95
2010
>60 days
$467,862.95
$467,862.95
2010
36 - 60 days
$432,416.84
$467,862.95
2011
<=30 days
$1,634,463.10
$742,348.24
2011
36 - 60 days
$1,710,892.85
$742,348.24
2011
>60 days
$742,348.24
$742,348.24
2012
36 - 60 days
$2,492,257.73
$5,410,974.02
2012
<=30 days
$5,410,974.02
$5,410,974.02
2012
>60 days
$1,434,506.99
$5,410,974.02
I am not sure why the max() window function picks up incorrect max values for some years as given.
Please enlighten me if I am doing something wrong
You need to move your formatting to only the first, outer select. By doing the formatting in the inner select, you are making max(money_raised) do string maximum of the formatted string, and "$742,348.24" is indeed greater than "$1,710,892.85", because the first characters are equal, and for the second characters, '7' is greater than '1'.
So:
select
year,
campaign_length_category,
concat('$', format(pledged,2,'en_US')) as money_raised
concat('$', format(max(pledged) over (partition by year),2,'en_US')) as max_money_raised
FROM
(
SELECT
year,
campaign_length_category,
sum(pledged) as pledged

Query to get records from quarter before current quarter in MySQL

I am trying to get records from quarter before current quarter in MySQL. But not able to figure out how the query will be constructed.
My MySQL table:
Here's my query to get records of current quarter (Jan 2020 to March 2020):
SELECT * FROM `customers` WHERE QUARTER(`customer_date`) = QUARTER(CURRENT_DATE)
AND YEAR(`customer_date`) = YEAR(CURRENT_DATE)
ORDER BY `customer_date` DESC
But when I am using following query to get records from quarter before current quarter i.e. records from Oct 2019 to Dec 2019, the query doesn't yield me correct result. Actually it fetches records of every quarter (Oct to Dec) of all previous years in the table:
SELECT * FROM `customers` WHERE QUARTER(`customer_date`) = QUARTER(CURRENT_DATE - INTERVAL 1 QUARTER)
ORDER BY `customer_date` DESC
I can't use YEAR in above query. If I use the year then query will fail when it will be run in the future e.g. in the quarter of Apr 2020 to Jun 2020.
How can this query be modified to get required result?
Try this will do:
select
*
from
customers
where
quarter(`customer_date`) = quarter(current_date - interval 1 quarter)
and year(customer_date) =
(case
when quarter(curdate()) > 1 then year(current_date)
else year(current_date)-1
end)
order by
`customer_date` desc
db<>fiddle

Calculate duration in days between start and end date for each year

I'm trying to calculate the duration in days for different years where:
startdate enddate duration (days)
2016-09-20 2018-09-20 730
Where i want the following outcome in one row as duration per year,
with the year as column name and the days as result:
2016 = 103 days - 2017 = 365 days - 2018 = 263 days
I couldn't find a specific solution except using DATEDIFF and entering the enddate of each year but this doesn't calculate the remaining days over next years. Anyone who can help me out finding a solution with MySQL?
Assuming (hoping) that you have a numbers table you can the following calculation:
SELECT startdate,
enddate,
number AS year_num,
DATEDIFF(
LEAST(DATE(CONCAT(number, '-12-31')), enddate),
GREATEST(DATE(CONCAT(number, '-01-01')), startdate)
) + 1 AS num_days
FROM t
INNER JOIN (
SELECT 2010 AS number UNION ALL
SELECT 2011 UNION ALL
SELECT 2012 UNION ALL
SELECT 2013 UNION ALL
SELECT 2014 UNION ALL
SELECT 2015 UNION ALL
SELECT 2016 UNION ALL
SELECT 2017 UNION ALL
SELECT 2018 UNION ALL
SELECT 2019
) AS numbers ON number BETWEEN YEAR(startdate) AND YEAR(enddate)

Is subquery the solution to this?

Here's the table structure and some sample data:
pID.....month.....year
27 .....3 .....2008
27 .....12 .....2012
31 .....6 .....2008
99 .....1 .....2006
42 .....1 .....2009
pID is the practiceID and month and year represent the date period they've entered data for. I need to grab the number of practices that have entered data for the first time in Oct 2012, Nov 2012, Dec 2012 and so on.
I tried the following query for Oct 2012:
SELECT *
FROM
IPIPKDIS
where
practiceID NOT IN (
SELECT practiceID
from
IPIPKDIS
where
year < 2012 and month < 10
)
and year = 2012
and month = 10
and measureCatRecID = 2
ORDER BY year, month;
but it's grabbing months and year less than 10/2012.
If I run the queries isolated (not as subquery) they both work fine.
Any ideas?
This summary query will yield the first (smallest) date in the table for each value of practiceID.
SELECT practiceID,
MIN(STR_TO_DATE( CONCAT(year, ' ', month), '%Y %m')) first_date
FROM IPIPKDIS
GROUP BY practiceID
If you want to retrieve then the whole row for the first reported month, you'd do a nested query like this:
SELECT *
FROM IPIPKDIS I
JOIN (
SELECT practiceID,
MIN(STR_TO_DATE( CONCAT(year, ' ', month), '%Y %m')) first_date
FROM IPIPKDIS
GROUP BY practiceID
) first ON ( first.practiceID = I.practiceID
AND STR_TO_DATE( CONCAT(I.year, ' ', I.month), '%Y %m') = first.first_date)
The trick to the second query is to use the JOIN to extract just the first-month rows from your table. We use date arithmetic to do the date comparisons.