Last 24 Month data - sql-server-2008

I want to display 24 Months data (By months name), but as shown in the query, i have to put 24 columns for each Month, that will make my query to long.
DECLARE #sql NVARCHAR(MAX) = 'SELECT [STORE] = ET_LIBELLE,
CONVERT(DECIMAL(15,0),SUM(CASE WHEN YEAR(GP_DATEPIECE) = YEAR(DATEADD(MONTH,-1,GETDATE()))
AND month(GP_DATEPIECE) = MONTH( DATEADD(MONTH,-1,GETDATE()))
THEN ISNULL([SALES], 0) ELSE 0 END)) AS ' + DATENAME(MONTH, DATEADD(MM,-1, GETDATE())) + ',
CONVERT(DECIMAL(15,0),SUM(CASE WHEN YEAR(GP_DATEPIECE) = YEAR(DATEADD(MONTH,-2,GETDATE()))
AND month(GP_DATEPIECE) = MONTH( DATEADD(MONTH,-2,GETDATE()))
THEN ISNULL([SALES], 0) ELSE 0 END)) AS ' + DATENAME(MONTH, DATEADD(MM,-2, GETDATE())) + ',
.
.
.
FROM PIECE
GROUP BY ET_LIBELLE'
EXEC sp_executesql #sql
RESULTS :
STORE - April - March - February - ..
--------------------------------------------------------
S1 - 5500 - 6530 - 4550 - ..
S2 - 2400 - 8740 - 9650 - ..
..
--------------------------------------------------------
Is there any solution to optimise and make my query fast please ?

This following script will return data by 12 month (JAN to DEC) but 1 Row per year. If it is acceptable, you can use this query. You can also set the Month number (#HowManyMonth = N) that for how many month you want to pick data.
DECLARE #HowManyMonth INT
SET #HowManyMonth = 24
DECLARE #StartDate DATE
SET #StartDate = CAST(DATEADD(DD,-(DAY(DATEADD(MM,-(#HowManyMonth-1),GETDATE()))-1),DATEADD(MM,-(#HowManyMonth-1),GETDATE())) AS DATE)
SELECT * FROM
(
SELECT Store,Sales,
YEAR(GP_DATEPIECE) YR,
CASE MONTH(GP_DATEPIECE)
WHEN 1 THEN 'JAN' WHEN 2 THEN 'FEB' WHEN 3 THEN 'MAR' WHEN 4 THEN 'APR' WHEN 5 THEN 'MAY' WHEN 6 THEN 'JUN'
WHEN 7 THEN 'JUL' WHEN 8 THEN 'AUG' WHEN 9 THEN 'SEP' WHEN 10 THEN 'OCT' WHEN 11 THEN 'NOV' WHEN 12 THEN 'DEC'
END MONTH
FROM PIECE
WHERE GP_DATEPIECE >= #StartDate
)AS P
PIVOT
(
SUM(Sales)
FOR MONTH IN ([JAN],[FEB],[MAR],[APR],[MAY],[JUN],[JUL],[AUG],[SEP],[OCT],[NOV],[DEC])
) PVT

Related

How to add month name with 0 total if data not exist in mysql?

I need to fetch data of the user's from created_date to till current data by month name and year with each month's total price.
Try 1:
SELECT MONTHNAME(start_time) month, YEAR(start_time) year, SUM(price) total
FROM table t1
WHERE t1.id= 33
GROUP BY YEAR(start_time), MONTH(start_time);
Output:
month year total
July 2019 360
September 2019 2160
October 2019 360
Expected output:
All month name and total will be 0 if data not exist.
month year total
Jan 2018 0
Feb 2018 0
...
Dec 2018 0
Jan 2019 0
Feb 2019 0
Mar 2019 0
Apr 2019 0
May 2019 0
Jun 2019 0
July 2019 360
Aug 2019 0
Sep 2019 2160
Oct 2019 360
Nov 2019 0
Dec 2019 0
After some RND I found one way and I have also tried that but now works.
Try 2:
SELECT IFNULL(SUM(ri.price),0) AS total, m.month
FROM (
SELECT 'Jan' AS MONTH
UNION SELECT 'Feb' AS MONTH
UNION SELECT 'Mar' AS MONTH
UNION SELECT 'Apr' AS MONTH
UNION SELECT 'May' AS MONTH
UNION SELECT 'Jun' AS MONTH
UNION SELECT 'Jul' AS MONTH
UNION SELECT 'Aug' AS MONTH
UNION SELECT 'Sep' AS MONTH
UNION SELECT 'Oct' AS MONTH
UNION SELECT 'Nov' AS MONTH
UNION SELECT 'Dec' AS MONTH
) AS m
LEFT JOIN table_u pu
ON MONTH(STR_TO_DATE(CONCAT(pu.created_date, '2019'),'%M %Y')) = MONTH(pu.created_date)
AND YEAR(pu.created_date) = '2019'
LEFT JOIN table ri
ON ri.id = pu.id
GROUP BY m.month
ORDER by 1+1;
Here is my refrence link.
Can anyone help me to fix this issue?
Thanks in advance.
Keep using LEFT JOIN (but only once) with a integer generator upto 12 (instead of month names, also easy using within the ORDER BY clause ), since you are able to get the month names by monthname() function. From your second query, I considered the current year. So, use :
SET #year=YEAR(NOW());
SELECT
MONTHNAME(STR_TO_DATE(concat(YEAR(NOW()),',',m.month,',1'),"%Y,%m,%d")) as month,
#year as year , SUM( COALESCE( pu.price, 0) ) as total
FROM (
SELECT rn as month
FROM
(
SELECT #rn := if(#i = #rn, #rn + 1, 1) as rn,
#i := #i+1
FROM information_schema.character_sets
JOIN (SELECT #i := 0, #rn := 0) as q_iter
LIMIT 12
) q
) AS m
LEFT JOIN table_u pu ON MONTH(pu.start_time) = m.month
AND YEAR(pu.start_time) = #year
AND ID = 33
GROUP BY m.month
ORDER by m.month;
Demo 1
Edit ( From the beginning of the year 2018 to the end of the current year ) :
SELECT MONTHNAME(
STR_TO_DATE(
CONCAT(m.year,',',if(mod(m.month,12)=0,12,mod(m.month,12)),',1'),"%Y,%m,%d")
)
as month,
year,
SUM( CASE WHEN YEAR(pu.start_time) = m.year AND MONTH(pu.start_time) = m.month
THEN
COALESCE( pu.price, 0)
ELSE
0
END ) as total
FROM
(
SELECT #i := if( #i = 12 , 1 , #i+1) as month,
#j := #j + 1,
#k:= if( #i = 1 and #j > 12, #k - 1, #k ) as year
FROM information_schema.character_sets
JOIN (SELECT #i := 0, #j := 0, #k:=YEAR(now())) as q_iter
) m
LEFT JOIN table_u pu ON MONTH(pu.start_time) = m.month
AND ID = 33
GROUP BY month, year
HAVING year >= ( SELECT MIN( YEAR(start_time) ) FROM table_u )
ORDER BY year, m.month;
Demo 2
If you have data in your table for all months, but just not for that id, then you can switch to conditional aggregation:
SELECT MONTHNAME(start_time) as month, YEAR(start_time) as year,
SUM(CASE WHEN t1.id = 33 THEN price ELSE 0 END) as total
FROM table t1
GROUP BY YEAR(start_time),MONTHNAME(start_time)
ORDER BY MIN(start_time);
Creating nonexistent year-month pairs by using user variables:
SELECT monthname(str_to_date(concat_ws(',',ym.month,'01,01'),'%m,%d,%y')) month
, ym.year year
, sum(price)
FROM table1 t1
RIGHT JOIN( SELECT #year := if(#month=12, #year+1, #year ) year
, #month := if(#month=12, 1 , #month+1) month
FROM table1
, ( SELECT #startYear := min(start_time)
, #endYear := year(now())
, #month := 12
, #year := min(start_time)-1
FROM table1
) t
WHERE (#year,#month) < (#endYear,12)
) ym
ON ym.year = year(t1.start_time)
AND ym.month = month(t1.start_time)
GROUP BY year(t1.start_time)
, month(t1.start_time)
The ym derived table is to provide year-month pairs starting on min year in table1 upto current year.
The innermost SELECT is for variables initialization.
You can try this:
SELECT to_char(start_time,'month')MONTH,to_char(start_time,'yyyy')YEAR,SUM(price)total
FROM TABLE_NAME
GROUP BY to_char(start_time,'month'),to_char(start_time,'yyyy')
Finally, I got the correct output which I want.
select
DATE_FORMAT(m1, '%M - %Y')
from
(
select
('2013-07-23')
+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<=NOW()
order by m1

MySQL stored procedure, while loop, union dataset

I'm new to stored procedures so I may be doing this wrong. I've created a stored procedure that brings back month name, month start and month end but I'm getting 12 separate datasets.
How can I combine this into one?
BEGIN
SET #i = 0;
SET #start_date = "2018-09-01";
SET #end_date = LAST_DAY('2018-09-01');
WHILE #i < 12 DO
SELECT MONTHNAME(#start_date) AS month, #start_date, #end_date;
SET #i = #i + 1;
SET #start_date = DATE_ADD(#start_date, INTERVAL 1 MONTH);
SET #end_date = LAST_DAY(DATE_ADD(#end_date, INTERVAL 1 MONTH));
END WHILE;
END
You don't need a stored procedure for that.
set #start_date = '2018-09-01';
with recursive months(m) as (
select 0
union all
select m + 1
from months
where m < 11
)
select #start_date + interval m.m month as first_day
, last_day(#start_date + interval m.m month) as last_day
from months m
order by m.m asc
Result:
first_day last_day
2018-09-01 2018-09-30
2018-10-01 2018-10-31
2018-11-01 2018-11-30
2018-12-01 2018-12-31
2019-01-01 2019-01-31
2019-02-01 2019-02-28
2019-03-01 2019-03-31
2019-04-01 2019-04-30
2019-05-01 2019-05-31
2019-06-01 2019-06-30
2019-07-01 2019-07-31
2019-08-01 2019-08-31
But you can ofcourse use that query in your SP too.
Rewrote #Paul Spiegel query so that I can understand how recursive queries work.
SET #start = "2018-09-01";
WITH RECURSIVE months (m)
AS (
SELECT 0
UNION ALL
SELECT m + 1
FROM months
WHERE m < 12
)
SELECT
MONTHNAME(DATE_ADD(#start, INTERVAL m MONTH)) AS month_name,
CONCAT(DATE_ADD(#start, INTERVAL m MONTH), " 00:00:00") AS start_of_month,
CONCAT(LAST_DAY(DATE_ADD(#start, INTERVAL m MONTH)), " 23:59:59") AS end_of_month
FROM months;
For MySQL versions before 8.0 , we can do something like this:
SELECT MONTHNAME(d.dt + INTERVAL i.n MONTH) AS month
, d.dt + INTERVAL i.n MONTH AS start_date
, LAST_DAY(d.dt + INTERVAL i.n MONTH) AS end_date
FROM ( SELECT '2018-09-01' + INTERVAL 0 MONTH AS dt ) d
CROSS
JOIN ( SELECT 0 AS 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 UNION ALL SELECT 10 UNION ALL SELECT 11
) i
ORDER
BY i.n

Select zero when no results in date range

I have a query that I am using to pull back the total costs per months for the previous 6 months of data. The issue I need to solve is when there is no records for a specific month, nothing is returned and only 5 months are shown.
I need to modify this query to always show the 6 months, even when there is no data for a specific month but I am unsure how to accomplish this.
select sum(cost),
CASE
WHEN MONTH(collection_date) = 1 THEN 'January'
WHEN MONTH(collection_date) = 2 THEN 'February'
WHEN MONTH(collection_date) = 3 THEN 'March'
WHEN MONTH(collection_date) = 4 THEN 'April'
WHEN MONTH(collection_date) = 5 THEN 'May'
WHEN MONTH(collection_date) = 6 THEN 'June'
WHEN MONTH(collection_date) = 7 THEN 'July'
WHEN MONTH(collection_date) = 8 THEN 'August'
WHEN MONTH(collection_date) = 9 THEN 'September'
WHEN MONTH(collection_date) = 10 THEN 'October'
WHEN MONTH(collection_date) = 11 THEN 'November'
WHEN MONTH(collection_date) = 12 THEN 'December'
ELSE 'NULL'
END AS datemodified
from invoices
WHERE collection_date >= DATE_SUB(now(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date asc;
Sample of the results with an empty month
COST Datemodified
300 September
200 November
200 December
Desired output
COST Datemodified
0 August
300 September
0 October
200 November
200 December
You can create fake month data and join your invoices table to it. Try this:
SELECT SUM(cost), months.name AS datemodified
FROM (SELECT 1 AS num, 'January' AS name
UNION SELECT 2, 'February'
UNION SELECT 3, 'March'
UNION SELECT 4, 'April'
UNION SELECT 5, 'May'
UNION SELECT 6, 'June'
UNION SELECT 7, 'July'
UNION SELECT 8, 'August'
UNION SELECT 9, 'September'
UNION SELECT 10, 'October'
UNION SELECT 11, 'November'
UNION SELECT 12, 'December') months
LEFT JOIN invoices.collection_date = months.num
WHERE collection_date >= DATE_SUB(NOW(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date ASC;
However, that gives you all the 12 months. To get only the 6 last months, you need to dynamically generate your fake month data:
SELECT SUM(cost),
CASE num WHEN 1 THEN 'January'
WHEN 2 THEN 'February'
WHEN 3 THEN 'March'
WHEN 4 THEN 'April'
WHEN 5 THEN 'May'
WHEN 6 THEN 'June'
WHEN 7 THEN 'July'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'October'
WHEN 11 THEN 'November'
WHEN 12 THEN 'December'
END AS datemodified
FROM (SELECT MONTH(NOW()) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 3 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 4 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 5 MONTH)) AS num) months
LEFT JOIN invoices.collection_date = months.num
WHERE collection_date >= DATE_SUB(NOW(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date ASC;

display month names as column names in mysql

I want to display year and month names as column names between two dates inside my procedure as below
year jan Feb ......Dec
---- ----- ----- -----
2016 val1 val2 val3
2017 val4 val5 val6
some one help me to do this
You could do something like this if i understand you question correctly to generate the desired resultset.
TIMESTAMPDIFF(DAY.... + INTERVAL 1 MONTH) makes it possible to calculate the days within a month.
Please note that months have to be right in every TIMESTAMPDIFF(DAY, '2016-01-01', '2016-01-01' + INTERVAL 1 MONTH) AS 'Jan' line and it should work.
Query
SELECT
'2016' AS YEAR
, TIMESTAMPDIFF(DAY, '2016-01-01', '2016-01-01' + INTERVAL 1 MONTH) AS 'Jan'
, TIMESTAMPDIFF(DAY, '2016-02-01', '2016-02-01' + INTERVAL 1 MONTH) AS 'Feb'
...
, TIMESTAMPDIFF(DAY, '2016-12-01', '2016-12-01' + INTERVAL 1 MONTH) AS 'Dec'
UNION ALL
SELECT
'2017' AS YEAR
, TIMESTAMPDIFF(DAY, '2017-01-01', '2017-01-01' + INTERVAL 1 MONTH) AS 'Jan'
, TIMESTAMPDIFF(DAY, '2017-02-01', '2017-02-01' + INTERVAL 1 MONTH) AS 'Feb'
...
, TIMESTAMPDIFF(DAY, '2017-12-01', '2017-12-01' + INTERVAL 1 MONTH) AS 'Dec'
Result
year Jan Feb Dec
------ ------ ------ --------
2016 31 29 31
2017 31 28 31
Or this query makes it eazier to add a new year and less code duplication.
Query
SELECT
years.year
, TIMESTAMPDIFF(DAY, CONCAT(years.year, '-01-01'), CONCAT(years.year, '-01-01') + INTERVAL 1 MONTH) AS 'Jan'
, TIMESTAMPDIFF(DAY, CONCAT(years.year, '-02-01'), CONCAT(years.year, '-02-01') + INTERVAL 1 MONTH) AS 'Feb'
...
, TIMESTAMPDIFF(DAY, CONCAT(years.year, '-12-01'), CONCAT(years.year, '-12-01') + INTERVAL 1 MONTH) AS 'Dec'
FROM (
SELECT
'2016' AS YEAR
UNION
ALL
SELECT
'2017' AS YEAR
)
AS years
Result
year Jan Feb Dec
------ ------ ------ --------
2016 31 29 31
2017 31 28 31

mysql, transpose/pivot row to column, variable selects

Hello again and thank you in advance for your help.
I've checked a few prior questions and couldn't find this exact situation.
I'm trying to transpose/pivot a row to column, but the results are based on a date function in the where clause, making my selects somewhat variable.
SELECT
DATE_FORMAT(packet_details.installDate,'%m-%d-%Y') as Install_Date,
Count(packet_details.installDate)
FROM
packet_details
WHERE
packet_details.installDate >= CURRENT_DATE - INTERVAL 7 DAY
AND packet_details.installDate "*lessthan*" CURRENT_DATE + INTERVAL 7 DAY
GROUP BY installDate
*lessthan symbol wont show here on Stack & i don't know how to fix it
Not sure if that makes sense so I included a fiddle: http://sqlfiddle.com/#!2/5b235/3/0
So something like this:
INSTALL_DATE COUNT
1/24/2013 2
1/25/2013 2
1/26/2013 2
1/27/2013 2
1/28/2013 2
1/29/2013 1
2/3/2013 1
2/4/2013 1
2/5/2013 5
2/6/2013 4
Turned into:
INSTALL_DATE 1/24/2013 1/25/2013 1/26/2013 1/27/2013 1/28/2013....
COUNT 2 2 2 2 2 1
SELECT Install_DATE,
MAX(CASE WHEN Install_DATE = '01-24-2013' THEN totalCount END) `01-24-2013`,
MAX(CASE WHEN Install_DATE = '01-25-2013' THEN totalCount END) `01-25-2013`,
MAX(CASE WHEN Install_DATE = '01-26-2013' THEN totalCount END) `01-26-2013`,
.......
FROM
(
SELECT DATE_FORMAT(packet_details.installDate,'%m-%d-%Y') as Install_Date,
Count(packet_details.installDate) totalCount
FROM packet_details
WHERE packet_details.installDate >= CURRENT_DATE - INTERVAL 7 DAY AND
packet_details.installDate < CURRENT_DATE + INTERVAL 7 DAY
GROUP BY installDate
) s
SQLFiddle Demo
For unknown number of Install_Date, a Dynamic Query is much preferred,
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(CASE WHEN Install_DATE = ''',
Install_Date,
''' then totalCount end) AS `', Install_Date, '`' )
) INTO #sql
FROM
(
SELECT DATE_FORMAT(packet_details.installDate,'%m-%d-%Y') as Install_Date,
Count(packet_details.installDate) totalCount
FROM packet_details
WHERE packet_details.installDate >= CURRENT_DATE - INTERVAL 7 DAY AND
packet_details.installDate < CURRENT_DATE + INTERVAL 7 DAY
GROUP BY installDate
) s;
SET #sql = CONCAT('SELECT Install_DATE, ', #sql, '
FROM
(
SELECT DATE_FORMAT(packet_details.installDate,''%m-%d-%Y'') as Install_Date,
Count(packet_details.installDate) totalCount
FROM packet_details
WHERE packet_details.installDate >= CURRENT_DATE - INTERVAL 7 DAY AND
packet_details.installDate < CURRENT_DATE + INTERVAL 7 DAY
GROUP BY installDate
) s');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo