Dhenn Dhenn - 2 months ago 15
PHP Question

MySQL or PHP Transform rows to columns

I have this kind of table:

enter image description here

the month are dynamically generated based on user input. The month can also be repeated. However, I want to transpose the month values to columns with a value of the amount but when trying it dynamically in mysql, it does not permit me because it can only return distinct values.

I also tried removing distinct from my code below but it is not working. Any thoughts?

CREATE DEFINER=`root`@`localhost` PROCEDURE `test`()
BEGIN
SET group_concat_max_len=2048;
SET @sql = NULL;

SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(month = ''',
month,
''', amount, NULL)) AS ',
month
)
) INTO @sql
FROM tmp_results;

SET @sql = CONCAT('SELECT r.account,
r.region, ',
@sql, '
FROM tmp_results r
LEFT JOIN accounts AS a
on r.account_id = a.id
GROUP BY r.account');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END


Tried to use the collection in laravel 5 and still does nothing, If you have answer for php, I welcome it also. I'm banging my head for days to solve this.

Answer

If you want to have seperate columns for your years too, you have to add the year (calculated from your column date) to your dynamic sql code:

CREATE DEFINER=`root`@`localhost` PROCEDURE `test`()
BEGIN
  SET group_concat_max_len=2048;
  SET @sql = NULL;

  SELECT GROUP_CONCAT(DISTINCT CONCAT(
      'MAX(IF(month = ''',
      month,
      ''' and year(date) = ',
      year(date),
      ', amount, NULL)) AS `',
      month,
      '_',
      year(date),
      '`'
    )
    order by date
  ) INTO @sql
  FROM tmp_results;

  if coalesce(@sql,'') != '' then
    set @sql = concat(', ', @sql);
  end if; 

  SET @sql = CONCAT(
    'SELECT r.account, 
     r.region ',  
     coalesce(@sql,''),
    ' FROM tmp_results r
     LEFT JOIN accounts AS a
     on r.account_id = a.id
     GROUP BY r.account, r.region');

  PREPARE stmt FROM @sql;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;
END

The columns will be named like January_2017, and I added an order by date, otherwise they would usually be unordered.

I added a group by r.region, otherwise it will not work if only_full_group_by is enabled on your server (which is the default value starting with MySQL 5.7).

And I added a test for empty tables (which would otherwise result in an error). If you don't need it and copy only parts of my code into yours, be aware of the missing comma after r.region in SET @sql = CONCAT('SELECT r.account, r.region ' compared to your code, you might have to add it again.

Since the code for each month has a length of about 80, you might have to increase group_concat_max_len to fit your largest possible query.

Comments