mysql, transpose/pivot row to column, variable selects - mysql

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

Related

MYSQL Subtracting two SELECT Queries

Using MYSQL, I have written two big SELECT queries combined by a UNION, to get 2 rows, where the first row is the count for the current month, and the second row is the count for the previous month. The Query is as follows:
select * from
(select count(*) as type1 from table_x where nationality_id = 23 and month(START_DATE) = month(now())) as t1,
(select count(*) as type2 from table_x where nationality_id = 24 and month(START_DATE) = month(now())) as t2,
(select count(*) as type3 from table_x where nationality_id = 25 and month(START_DATE) = month(now())) as t3,
(select count(*) as type4 from table_x where nationality_id = 26 and month(START_DATE) = month(now())) as t4
UNION
select * from
(select count(*) as type1 from table_x where nationality_id = 23 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t1,
(select count(*) as type2 from table_x where nationality_id = 24 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t2,
(select count(*) as type3 from table_x where nationality_id = 25 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t3,
(select count(*) as type4 from table_x where nationality_id = 26 and month(START_DATE) = month(now() - INTERVAL 1 MONTH)) as t4
I want to add a third row, which is the difference between row 2 and row 1.
How can I do this with my current query?
You are obviously doing a compare between current and prior month. So, I would start with my inner pre-query aggregate getting only those transactions >= the first of the prior month AND the records within the nationality IDs you are looking for.
The inner date_sub() of DAYOFMONTH() -1 day gives you the first of the CURRENT month. By subtracting one more month, gives you the first of the LAST month.
Now you can aggregate the totals per nationality compared to current month or not. That inner query gives you all begin and end counts. Now that is wrapped to the outer and you can get all the counts in addition to the differences... Obviously you can change the column names respectively.
select
PQ.*,
PQ.ThisMonth23 - PQ.LastMonth23 = Diff23,
PQ.ThisMonth24 - PQ.LastMonth24 = Diff24,
PQ.ThisMonth25 - PQ.LastMonth25 = Diff25,
PQ.ThisMonth26 - PQ.LastMonth26 = Diff26
from
( select
sum( case when t.Nationality_id = 23 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth23,
sum( case when t.Nationality_id = 24 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth24,
sum( case when t.Nationality_id = 25 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth25,
sum( case when t.Nationality_id = 26 and month( t.StartDate ) = month( now()) then 1 else 0 end ) ThisMonth26,
sum( case when t.Nationality_id = 23 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth23,
sum( case when t.Nationality_id = 24 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth24,
sum( case when t.Nationality_id = 25 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth25,
sum( case when t.Nationality_id = 26 and month( t.StartDate ) != month( now()) then 1 else 0 end ) LastMonth26
from
table_x t
where
t.StartDate >= date_sub( date_sub( t.StartDate, interval DAYOFMONTH( t.StartDate ) -1 DAY ), interval 1 MONTH )
AND t.Nationality_id IN ( 23, 24, 25, 26 )
) PQ
I would just add that your query might be getting more than you think... You are asking for ALL records Ex: January REGARDLESS of the year, compared to ALL records December REGARDLESS of the year because all you are qualifying is based on the MONTH() and no YEAR() consideration. I am explicitly querying back only current and prior month.

Last 24 Month data

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

coverting column to row using my sql

i have following mysql query:
SELECT
a.id, a.sbu_name, b.month,b.week, b.DateRange, b.planned
FROM
(SELECT
id, sbu_name
FROM
tbl_sbu) a
LEFT JOIN
(SELECT
sbu_id,
month,
WEEK(month) week,
CONCAT(DATE_FORMAT(DATE_ADD(month, INTERVAL (1 - DAYOFWEEK(month)) DAY), '%Y-%m-%e'), ' TO ', DATE_FORMAT(DATE_ADD(month, INTERVAL (7 - DAYOFWEEK(month)) DAY), '%Y-%m-%e')) AS DateRange,
SUM(amount) / 100000 AS planned
FROM
tbl_collection_planed
WHERE
DATE_FORMAT(month, '%Y-%m') = '2016-08'
GROUP BY DateRange) b ON b.sbu_id = a.id order by week asc
The output of above query is:
I want my output as follows:
This is a table pivot question, if just for this case, you can try following query;)
SELECT
sbu_name,
IF(DateRange = '2016-08-7 TO 2016-08-13', planned, '') AS `2016-08-7 TO 2016-08-13`,
IF(DateRange = '2016-08-14 TO 2016-08-20', planned, '') AS `2016-08-14 TO 2016-08-20`,
IF(DateRange = '2016-08-21 TO 2016-08-27', planned, '') AS `2016-08-21 TO 2016-08-27`,
IF(DateRange = '2016-08-28 TO 2016-09-3', planned, '') AS `2016-08-28 TO 2016-09-3`
FROM (
SELECT
a.id, a.sbu_name, b.month,b.week, b.DateRange, b.planned
FROM
(SELECT
id, sbu_name
FROM
tbl_sbu) a
LEFT JOIN
(SELECT
sbu_id,
month,
WEEK(month) week,
CONCAT(DATE_FORMAT(DATE_ADD(month, INTERVAL (1 - DAYOFWEEK(month)) DAY), '%Y-%m-%e'), ' TO ', DATE_FORMAT(DATE_ADD(month, INTERVAL (7 - DAYOFWEEK(month)) DAY), '%Y-%m-%e')) AS DateRange,
SUM(amount) / 100000 AS planned
FROM
tbl_collection_planed
WHERE
DATE_FORMAT(month, '%Y-%m') = '2016-08'
GROUP BY DateRange) b ON b.sbu_id = a.id order by week asc
) t
GROUP BY sub_name
If this DateRange has dynamic values, you have to do this with a dynamic sql.
EDITED(dynamic sql)
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(DateRange = ''',
CONCAT(DATE_FORMAT(DATE_ADD(month, INTERVAL (1 - DAYOFWEEK(month)) DAY), '%Y-%m-%e'), ' TO ', DATE_FORMAT(DATE_ADD(month, INTERVAL (7 - DAYOFWEEK(month)) DAY), '%Y-%m-%e')),
''', planned, '')) AS `',
CONCAT(DATE_FORMAT(DATE_ADD(month, INTERVAL (1 - DAYOFWEEK(month)) DAY), '%Y-%m-%e'), ' TO ', DATE_FORMAT(DATE_ADD(month, INTERVAL (7 - DAYOFWEEK(month)) DAY), '%Y-%m-%e')), '`'
)
) INTO #sql
FROM tbl_collection_planed
WHERE DATE_FORMAT(month, '%Y-%m') = '2016-08'
;
SET #sql = CONCAT('SELECT sbu_name, ', #sql, ' FROM (
SELECT
a.id, a.sbu_name, b.month,b.week, b.DateRange, b.planned
FROM
(SELECT
id, sbu_name
FROM
tbl_sbu) a
LEFT JOIN
(SELECT
sbu_id,
month,
WEEK(month) week,
CONCAT(DATE_FORMAT(DATE_ADD(month, INTERVAL (1 - DAYOFWEEK(month)) DAY), \'%Y-%m-%e\'), \' TO \', DATE_FORMAT(DATE_ADD(month, INTERVAL (7 - DAYOFWEEK(month)) DAY), \'%Y-%m-%e\')) AS DateRange,
SUM(amount) / 100000 AS planned
FROM
tbl_collection_planed
WHERE
DATE_FORMAT(month, \'%Y-%m\') = \'2016-08\'
GROUP BY DateRange) b ON b.sbu_id = a.id order by week asc
) t
GROUP BY sub_name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Try this
**select * from (**
SELECT
a.id, a.sbu_name, b.month,b.week, b.DateRange, b.planned
FROM
(SELECT
id, sbu_name
FROM
tbl_sbu) a
LEFT JOIN
(SELECT
sbu_id,
month,
WEEK(month) week,
CONCAT(DATE_FORMAT(DATE_ADD(month, INTERVAL (1 - DAYOFWEEK(month)) DAY), '%Y-%m-%e'), ' TO ', DATE_FORMAT(DATE_ADD(month, INTERVAL (7 - DAYOFWEEK(month)) DAY), '%Y-%m-%e')) AS DateRange,
SUM(amount) / 100000 AS planned
FROM
tbl_collection_planed
WHERE
DATE_FORMAT(month, '%Y-%m') = '2016-08'
GROUP BY DateRange) b ON b.sbu_id = a.id **) as b group by DateRange**

How to select 6 months of data in MySQL, grouped between the 22nd and 21st of the next month

I have a table that contains the amount of data used each day, it looks something like this:
date | bytes
------------------
2014-01-1 | 12345
2014-01-2 | 56789
2014-01-3 | 78901
...
2014-02-1 | 12345
2014-02-2 | 56789
2014-02-3 | 78901
...
What I need to do is get the last 6 monthly totals, however the month must start on the 22nd day of the month and finish on the 21st day of the following month. For the current month it should start on the 22nd and finish today.
The best I can come up with is the following, the problem is - it is very messy and doesn't seem to give the correct result.
SELECT monthname(`date`),sum(`bytes`)
FROM `trafficDaily`
WHERE `date` between STR_TO_DATE( CONCAT( "22,", MONTH( NOW( ) )-6 , ",", YEAR( NOW( ) ) ) , "%d,%m,%Y" )
and STR_TO_DATE( CONCAT( "21,", MONTH( NOW( ) ) , ",", YEAR( NOW( ) ) ) , "%d,%m,%Y" )
group by month(DATE_SUB(`date`, INTERVAL 21 DAY))
order by `date`
Thank you in advance for your help.
You can do so by using user-defined variables to track the the change of month i.e in your case month starts from 21st
SELECT
MONTHNAME(STR_TO_DATE(group_day, '%m')) month_name ,
SUM(`bytes`) `sum`
FROM (
SELECT *,
#changemonth:= CASE
WHEN DAY(`date`) > 21
THEN #month
WHEN MONTH(`date`) <> #month
THEN #month
ELSE #month - 1
END group_day,
#month:= MONTH(`date`)
FROM
t ,(SELECT #changemonth:=0,
#month:= (SELECT MONTH(`date`) FROM t
WHERE `date` > NOW() - INTERVAL 6 MONTH ORDER BY `date` LIMIT 1) aa
) tt
WHERE `date` > NOW() - INTERVAL 6 MONTH
ORDER BY `date`
) a
GROUP BY group_day
Demo for last 3 months
Edit from comments for the case when January lies in last 6 month period
SELECT
MONTHNAME(
STR_TO_DATE(
CASE WHEN group_day < 1
THEN 12 ELSE group_day
END, '%m'
)
) month_name ,
SUM(`bytes`) `sum`
FROM (
SELECT *,
#changemonth:= CASE
WHEN DAY(`date`) > 21
THEN #month
WHEN MONTH(`date`) <> #month
THEN #month
ELSE #month - 1
END group_day,
#month:= MONTH(`date`)
FROM
t ,(SELECT #changemonth:=0,
#month:= (SELECT MONTH(`date`) FROM t
WHERE `date` > NOW() - INTERVAL 6 MONTH ORDER BY `date` LIMIT 1) aa
) tt
WHERE `date` > NOW() - INTERVAL 6 MONTH
ORDER BY `date`
) a
GROUP BY group_day
Demo with January
You have at least two options:
1st option
Create a calendar table and assign the 'business month' to each days. You can prepare your table for a long time period, then you can join to that table by date and you can do the grouping. If you have to do this calculation regularry, this is a good solution. (You can upgrade and use the calendar table to do several tasks)
2nd option
You can calculate the 'business month' by the date using the following query. (Please note, that I did not tested this query, so there could be typos).
SELECT
CASE
WHEN DAY(date) >= 22 THEN CONCAT(YEAR(date), '-', MONTH(date))
ELSE CONCAT(YEAR(date - INTERVAL 1 MONTH), '-', MONTH(date - INTERVAL 1 MONTH))
END AS m,
SUM(bytes)
FROM
log -- Use your table name instead :)
GROUP BY
CASE
WHEN DAY(date) >= 22 THEN CONCAT(YEAR(date), '-', MONTH(date))
ELSE CONCAT(YEAR(date - INTERVAL 1 MONTH), '-', MONTH(date - INTERVAL 1 MONTH))
END
You can adjust the calculation to your needs.

SQL View to show interval of 12 months and by year

Right now one of the other programmers wrote this view to show interval of 6months. How do i write this so that it shows interval of 12 months grouped by month but only for year 2011
I'd like to copy it for a separate view of 12 months grouped by month but only for year 2012
CREATE ALGORITHM=UNDEFINED DEFINER=`root`#`%`
SQL SECURITY DEFINER VIEW `vw_dash_bymonth`AS
select
month(from_unixtime(`tbl_services`.`datetime`)) AS` month1`,
date_format(from_unixtime(`tbl_services`.`datetime`),'%Y') AS` year1`,
date_format(from_unixtime(`tbl_services`.`datetime`),'%d') AS `day1`,
`tbl_services`.`datetime` AS `realdate`,sum(`tbl_services`.`gallons`) AS `gallons`,
count(0) AS `service`,
round(avg(`tbl_services`.`gallons`),1) AS `average`
from `tbl_services`
where (from_unixtime(`tbl_services`.`datetime`) > (now() - interval 6 month))
group by month(from_unixtime(`tbl_services`.`datetime`))
If you look at the where clause
where (from_unixtime(`tbl_services`.`datetime`) > (now() - interval 6 month))
I believe this is getting the dates from everything from 6 months ago until today. If you want 12 months in 2011 I think you could replace that line with something like:
where (from_unixtime(`tbl_services`.`datetime`) >= DATE('2011-01-01 00:00:00'))
AND (from_unixtime(`tbl_services`.`datetime`) < DATE('2012-01-01 00:00:00'))
Although I don't know MySQL (just SQLServer) so if this doesn't work, hopefully someone else can tell me where I went wrong.
It can be simplified to:
where (from_unixtime(`tbl_services`.`datetime`) >= '2011-01-01')
AND (from_unixtime(`tbl_services`.`datetime`) < '2012-01-01')
SELECT DISTINCT FROM_UNIXTIME('date','%m-%Y') AS month,
COUNT(`id`) AS count
FROM 'blog'
GROUP BY month
ORDER BY month DESC LIMIT 0,12
SELECT
MONTH( UNIX_TIMESTAMP( t.`datetime`) ) AS month1,
DATE_FORMAT( UNIX_TIMESTAMP( t.`datetime`), '%Y' ) AS year1,
DATE_FORMAT( UNIX_TIMESTAMP( t.`datetime`), '%d') AS day1,
t.`datetime` AS realdate,
SUM(t.gallons) AS gallons,
COUNT(*) AS `service`,
ROUND( AVG( t.gallons ), 1 ) AS `average`
FROM
tbl_services AS t
CROSS JOIN
( SELECT 2011 AS YearToCheck
) AS c
WHERE t.`datetime` >= UNIX_TIMESTAMP( MAKEDATE( YearToCheck, 1 ) )
AND t.`datetime` < UNIX_TIMESTAMP( MAKEDATE( YearToCheck+1, 1 ) )
GROUP BY MONTH( FROM_UNIXTIME( t.`datetime` ) )