Calculating Cumulative values using MySQL - mysql

I have this table:
Month_Year MV MI
----------------------------------------------------------
August 2016 3 100
October 2016 2 150
September 2016 1 100
January 2017 4 200
I need to create the next output table
Month_Year MV AMV MI AMI
----------------------------------------------------------
January 2016 0 0 0 0
...
July 2016 0 0 0 0
August 2016 3 3 100 100
September 2016 1 4 100 200
October 2016 2 6 150 350
November 2016 0 6 0 350
December 2016 0 6 0 350
January 2017 4 10 200 550
At month M, for column AMV, AMV is the ccumulative value of all the AV ones before that month and month M. For example, AMV is 4 on 'September 2016' because in 'August 16' AV is 3 and 1 for 'September 2016'. Similarly for AMI. How can this be done?. Notice that the Month_Year column is not necessarily ordered. Thanks
ADDITIONAL INFO
After using the DATE_FORMAT(original_Date, '%Y %m') I was able to convert the first table to:
Month_Year MV MI
----------------------------------------------------------
2016 08 3 100
2016 10 2 150
2016 09 1 100
2017 01 4 200
Could this simplify the problem? Why not to use DATE_FORMAT(original_Date, '%m %Y')?

The month-year format is a big big NO, and you have to somehow overcome it. For this example I create a MEMORY table months, and I use several joins with this table.
create table months (month_year_int int, month_year varchar(30)) engine=memory;
insert months
select 201601 as month_year_int, 'January 2016' as month_year
union all select 201602, 'February 2016'
union all select 201603, 'March 2016'
union all select 201604, 'April 2016'
union all select 201605, 'May 2016'
union all select 201606, 'June 2016'
union all select 201607, 'July 2016'
union all select 201608, 'August 2016'
union all select 201609, 'September 2016'
union all select 201610, 'October 2016'
union all select 201611, 'November 2016'
union all select 201612, 'December 2016'
union all select 201701, 'January 2017'
;
All these would have been avoided if you had used a proper model for your data. Anyway, this is a solution whis does not use the My-Sql proprietary variable pattern (a rextester demo here):
select
x.month_year,
coalesce(t.mv, 0) MV,
sum(y.mv) as AMV,
coalesce(t.mi, 0) AMV,
sum(y.mi) as AMI from
(
select
m.month_year_int,
m.month_year,
coalesce(t.mv, 0) as mv,
coalesce(t.mi) as mi
from months m left join test t on m.month_year = t.month_year
) x
left join
(
select
m.month_year_int,
m.month_year,
coalesce(t.mv, 0) as mv,
coalesce(t.mi) as mi
from months m
left join test t on m.month_year = t.month_year
) y on x.month_year_int > y.month_year_int - 1
left join test t on x.month_year = t.month_year
group by x.month_year_int
order by x.month_year_int
;

Related

How to get date increments in between start date and end date in MYSQL

I have sample dates in a table and what I need to get is each of the months between the start date and end date.
sample :
ID Startdate EndDate
1 01-01-2019 01-03-2019
2 01-08-2019 01-02-2020
I need to fetch months and year from these dates.
Desired output :
ID Dates
1 January 2019
1 February 2019
1 March 2019
2 August 2019
2 September 2019
2 October 2019
2 November 2019
2 December 2019
2 January 2020
2 February 2020
How cah I achieve this in MySQL and how to do increment or any loop kind of operation. On the query side I'm not getting any idea to move on this.
Here are a couple of ways to achieve this. The first will only work on MySQL 8+ and uses a recursive CTE to generate the months between StartDate and EndDate:
WITH RECURSIVE CTE AS (
SELECT ID, Startdate AS d, EndDate
FROM dates
UNION ALL
SELECT ID, d + INTERVAL 1 MONTH, EndDate
FROM CTE
WHERE d < EndDate
)
SELECT ID, DATE_FORMAT(d, '%M %Y') AS Dates
FROM CTE
ORDER BY ID, d
The second (which will run on any version of MySQL) uses a numbers table (in this case numbers from 0 to 99, allowing for a range of up to 99 months between StartDate and EndDate; if you need longer, adding more tables to the CROSS JOIN will increase that range by a factor of 10 for each table added) to generate the list of months difference, this is then JOINed to the original table so that the generated date Startdate + INTERVAL n.n MONTH is less than or equal to EndDate:
SELECT ID, DATE_FORMAT(Startdate + INTERVAL n.n MONTH, '%M %Y') AS Dates
FROM dates
JOIN (
SELECT n10.n * 10 + n1.n * 1 AS n
FROM (
SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n10
CROSS JOIN (
SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
) n1
) n ON Startdate + INTERVAL n.n MONTH <= EndDate
ORDER BY ID, Startdate + INTERVAL n.n MONTH
Having generated our list of dates, we format it using DATE_FORMAT and a format string of %M %Y. For both queries the output is:
ID Dates
1 January 2019
1 February 2019
1 March 2019
2 August 2019
2 September 2019
2 October 2019
2 November 2019
2 December 2019
2 January 2020
2 February 2020
Demo on dbfiddle

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)

How to order months between two years in SSRS

I have the following table in SQL:
UniqueUsers YEAR MONTH MONTH2
NULL NULL January 1
NULL NULL February 2
NULL NULL July 7
NULL NULL August 8
NULL NULL September 9
NULL NULL October 10
NULL NULL November 11
NULL NULL December 12
1 2016 March 3
2 2016 April 4
2 2016 May 5
1 2016 June 6
In SSRS I have a table grouped by Month with 4 parameters: 1 for Year1 and 1 for Month1; 1 for Year2 and 1 for Month2.
For example, if I select Year1 = 2015 and Month1 = November and December and Year2 = 2016 and Month2 = January and February I need to show the months like this even if the results have NULL values:
Nov Dec Jan Feb Mar Apr May Jun Jul Aug Sep Oct
This is the query than I use:
SELECT *
FROM
(
SELECT
[QUARTER]
,[CARRIER]
,[PLANGROUP]
,[UniqueUsers]
,[YEAR]
,A.[MONTH]
,A.[MONTH2]
FROM [ReportUnit].[dbo].[Month_List] A
LEFT JOIN
(SELECT
[YEAR]
,[MONTH2]
,[CARRIER]
,[PLANGROUP]
,[QUARTER]
,COUNT(DISTINCT [CONTRACT_NUM]) AS [UniqueUsers]
FROM
(
SELECT *
FROM
(
SELECT
MIN([YEAR]) AS [YEAR]
,MIN([MONTH2]) AS [MONTH2]
,DATENAME(MONTH, DATEADD(MONTH, MIN([MONTH2])-1, CAST('2017-01-01' AS datetime))) AS [MONTH]
,[CARRIER]
,[PLANGROUP]
,MIN([QUARTER]) AS [QUARTER]
,[CONTRACT_NUM]
FROM MyTable
WHERE
[YEAR] = #YEAR1
AND [PLANGROUP] IN (#ACCOUNT)
GROUP BY [PLANGROUP],[CARRIER],[CONTRACT_NUM]
) Q1
WHERE
[MONTH] IN (#MONTH1) --THIS IS MONTH NAME PARAMETER IN SSRS
--[MONTH2] IN (11,12)
UNION ALL
SELECT *
FROM
(
SELECT
MIN([YEAR]) AS [YEAR]
,MIN([MONTH2]) AS [MONTH2]
,DATENAME(MONTH, DATEADD(MONTH, MIN([MONTH2])-1, CAST('2017-01-01' AS datetime))) AS [MONTH]
,[CARRIER]
,[PLANGROUP]
,MIN([QUARTER]) AS [QUARTER]
,[CONTRACT_NUM]
FROM MyTable
WHERE
[YEAR] = #YEAR2
AND [PLANGROUP] IN (#ACCOUNT)
GROUP BY [PLANGROUP],[CARRIER],[CONTRACT_NUM]
) Q1
WHERE
[MONTH] IN (#MONTH2) --THIS IS MONTH NAME PARAMETER IN SSRS
--[MONTH2] IN (1,2,3,4,5,6,7,8,9,10)
) Q1
GROUP BY [YEAR],[MONTH2],[PLANGROUP],[CARRIER],[QUARTER]
) B ON A.MONTH2 = B.MONTH2
) Q4
ORDER BY [PLANGROUP], [YEAR], [MONTH2], [MONTH]
The best way to handle these kind of things is create a date table. How detailed that is, is down to your requirements. In your case you may only need months and years, although if you data is more detailed you may need all dates. You can then use this table and left join your main data to it.
The other option is to build a temp table in your dataset with your months and years in it.
I didn't quite understand what parameters you are passing so I'll just have a guess, hopefully this will give you enough to go on anyway. This assumes you are only ever working with data from no more than 2 years, if not then you are probably better to go down the permanent date table route...
So, if you pass in 4 parameters to your dataset query as 2015; 11; 2016; 10 (Nov'15 to Oct'16)
The your dataset query would look something like.
/* uncomment for testing on SSMS only
DECLARE #Year1 int = 2015
DECLARE #Year2 int = 2016
DECLARE #Month1 int = 11
DECLARE #Month2 int = 10
*/
/*Create temp Dates Table*/
DECLARE #d TABLE([Year] int, [Month] int)
INSERT INTO #d
SELECT #Year1, 1 UNION
SELECT #Year1, 2 UNION
SELECT #Year1, 3 UNION
SELECT #Year1, 4 UNION
SELECT #Year1, 5 UNION
SELECT #Year1, 6 UNION
SELECT #Year1, 7 UNION
SELECT #Year1, 8 UNION
SELECT #Year1, 9 UNION
SELECT #Year1, 10 UNION
SELECT #Year1, 11 UNION
SELECT #Year1, 12 UNION
SELECT #Year2, 1 UNION
SELECT #Year2, 2 UNION
SELECT #Year2, 3 UNION
SELECT #Year2, 4 UNION
SELECT #Year2, 5 UNION
SELECT #Year2, 6 UNION
SELECT #Year2, 7 UNION
SELECT #Year2, 8 UNION
SELECT #Year2, 9 UNION
SELECT #Year2, 10 UNION
SELECT #Year2, 11 UNION
SELECT #Year2, 12
SELECT d.[Year], d.[Month], t.myColumns
FROM #d d
LEFT JOIN myTable t on d.[Year] = t.[Year] and d.[Month] = t.[Month]
WHERE
((t.[Year] *100) + t.[Month]) between (#Year1 *100 + #Month1) and (#Year2 *100 + #Month2)
ORDER by d.[Year], d.[Month]
This is the query than I use:
SELECT *
FROM
(
SELECT
[QUARTER]
,[CARRIER]
,[PLANGROUP]
,[UniqueUsers]
,[YEAR]
,A.[MONTH]
,A.[MONTH2]
FROM [ReportUnit].[dbo].[Month_List] A
LEFT JOIN
(SELECT
[YEAR]
,[MONTH2]
,[CARRIER]
,[PLANGROUP]
,[QUARTER]
,COUNT(DISTINCT [CONTRACT_NUM]) AS [UniqueUsers]
FROM
(
SELECT *
FROM
(
SELECT
MIN([YEAR]) AS [YEAR]
,MIN([MONTH2]) AS [MONTH2]
,DATENAME(MONTH, DATEADD(MONTH, MIN([MONTH2])-1, CAST('2017-01-01' AS datetime))) AS [MONTH]
,[CARRIER]
,[PLANGROUP]
,MIN([QUARTER]) AS [QUARTER]
,[CONTRACT_NUM]
FROM MyTable
WHERE
[YEAR] = #YEAR1
AND [PLANGROUP] IN (#ACCOUNT)
GROUP BY [PLANGROUP],[CARRIER],[CONTRACT_NUM]
) Q1
WHERE
[MONTH] IN (#MONTH1) --THIS IS MONTH NAME PARAMETER IN SSRS
--[MONTH2] IN (11,12)
UNION ALL
SELECT *
FROM
(
SELECT
MIN([YEAR]) AS [YEAR]
,MIN([MONTH2]) AS [MONTH2]
,DATENAME(MONTH, DATEADD(MONTH, MIN([MONTH2])-1, CAST('2017-01-01' AS datetime))) AS [MONTH]
,[CARRIER]
,[PLANGROUP]
,MIN([QUARTER]) AS [QUARTER]
,[CONTRACT_NUM]
FROM MyTable
WHERE
[YEAR] = #YEAR2
AND [PLANGROUP] IN (#ACCOUNT)
GROUP BY [PLANGROUP],[CARRIER],[CONTRACT_NUM]
) Q1
WHERE
[MONTH] IN (#MONTH2) --THIS IS MONTH NAME PARAMETER IN SSRS
--[MONTH2] IN (1,2,3,4,5,6,7,8,9,10)
) Q1
GROUP BY [YEAR],[MONTH2],[PLANGROUP],[CARRIER],[QUARTER]
) B ON A.MONTH2 = B.MONTH2
) Q4
ORDER BY [PLANGROUP], [YEAR], [MONTH2], [MONTH]
And I don`t know how I can join it in your temp Table

quarter year wise result filteration in sql

I have a table having structure as follows
id cust_id target month year fiscal_ID
1 234 50 4 2013 1
2 234 50 5 2013 1
3 234 50 6 2013 1
4 234 150 7 2013 1
5 234 150 8 2013 1
6 234 150 9 2013 1
I need to get the result as follows
cust_id target quarter year fiscal_ID
234 150/450 Q1/Q2 2013 1
months 4,5,6 in Q1, 7,8,9 in Q2 etc
In order to concatenate the values from the multiple rows together, you will need to implement FOR XML PATH, similar to this:
;with cte as
(
select t.cust_id,
sum(target) target,
d.qtr,
t.year,
t.fiscal_id
from yourtable t
inner join
(
select 4 mth, 'Q1' qtr union all
select 5 mth, 'Q1' qtr union all
select 6 mth, 'Q1' qtr union all
select 7 mth, 'Q2' qtr union all
select 8 mth, 'Q2' qtr union all
select 9 mth, 'Q2'
) d
on t.month = d.mth
group by t.cust_id, d.qtr, t.year, t.fiscal_id
)
select distinct cust_id,
STUFF(
(SELECT ' / ' + cast(c2.target as varchar(10))
FROM cte c2
where c1.cust_id = c1.cust_id
and c1.year = c2.year
and c1.fiscal_id = c2.fiscal_id
FOR XML PATH (''))
, 1, 2, '') AS target,
STUFF(
(SELECT ' / ' + c2.qtr
FROM cte c2
where c1.cust_id = c1.cust_id
and c1.year = c2.year
and c1.fiscal_id = c2.fiscal_id
FOR XML PATH (''))
, 1, 2, '') AS qtr,
year,
fiscal_id
from cte c1;
See SQL Fiddle with Demo

How to get a list of months between two dates in mysql

I hve to get the list of months between two dates in mysql.
For Example:My Input is
From date 23-01-2013
To Date 01-04-2014
Output Should be
Jan 2013,
Feb 2013,
March 2013,
.
.
.
Jan 2014,
Feb 2014,
Mar 2014,
Apr 2014.
SQLFiddle demo
select
DATE_FORMAT(m1, '%b %Y')
from
(
select
('2013-01-23' - INTERVAL DAYOFMONTH('2013-01-23')-1 DAY)
+INTERVAL m MONTH as m1
from
(
select #rownum:=#rownum+1 as m from
(select 1 union select 2 union select 3 union select 4) t1,
(select 1 union select 2 union select 3 union select 4) t2,
(select 1 union select 2 union select 3 union select 4) t3,
(select 1 union select 2 union select 3 union select 4) t4,
(select #rownum:=-1) t0
) d1
) d2
where m1<='2014-04-01'
order by m1
this is a practical solution, it's not so 'elegant' if you wanna see it that way, but it works, and you can make it a function and/or a stored procedure, just with a couple of parameters...
first, we need a table with some records, any table.
we're gonna use this table, just as a row number table. (you'll need as many rows for the same amount of months you wanna display, having a large table is better) >>
SELECT CONCAT(table_schema, '.', table_name) as schema_table, table_rows
FROM information_schema.TABLES
order by 2 desc limit 0,100
this will tell you, the top 100 tables with most records on your instance, i'm using the mysql.help table for this example, by default, this comes with some thousand records, and it's allways there...
set #start_date = '2013-01-23';
set #end_date = '2014-04-01';
set #months = -1;
select DATE_FORMAT(date_range,'%M, %Y') AS result_date from (
select (date_add(#start_date, INTERVAL (#months := #months +1 ) month)) as date_range
from mysql.help_topic a limit 0,1000) a
where a.date_range between #start_date and last_day(#end_date);
explained:
1. set date variales
2. set month value, for adding months
3. select for each row, a date (we add a month and increment the month variable on the same row)
4. filter dates that are between range
5. output formated dates.
this is the final output>>
January, 2013
February, 2013
March, 2013
April, 2013
May, 2013
June, 2013
July, 2013
August, 2013
September, 2013
October, 2013
November, 2013
December, 2013
January, 2014
February, 2014
March, 2014
April, 2014
Try this one:
select aDate from (
select #maxDate - interval (a.a+(10*b.a)+(100*c.a)+(1000*d.a)) day aDate from
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) a, /*10 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) b, /*100 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) c, /*1000 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) d, /*10000 day range*/
(select #minDate := '2001-01-01', #maxDate := '2002-02-02') e
) f
where aDate between #minDate and #maxDate
This SQL from the following helped in my MYSQL. You can update the curdate() value as per your need to get the list of dates of any month.
link: (https://www.programmersought.com/article/26156494713/)
SELECT DATE_FORMAT(DATE_SUB(last_day(curdate()), INTERVAL xc-1 day), '%Y-%m-%d') as date
FROM (
SELECT #xi:=#xi+1 as xc from
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6) xc1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6) xc2,
(SELECT #xi:=0) xc0
) xcxc) x0 where x0.date >= (select date_add(curdate(),interval-day(curdate())+1 day))