Tabla dinámica de MySQL con encabezados dinámicos basados en datos de una sola columna

Estoy tratando de escribir una consulta para crear una 'tabla' de datos de la siguiente manera:

SELECT cs.`category_id`, cs.`ProcessDate`, cs.`PercentChange`
  FROM `Category_Statistics` cs
 WHERE cs.`ProcessDate` >= '2011-05-10'
   AND cs.`ProcessDate` <= '2011-05-14'

Que devolvería algo como:

CategoryId  |  ProcessDate  | PercentChange
-------------------------------------------
category_4  |  2011-05-10   |      10
category_4  |  2011-05-11   |      18
category_4  |  2011-05012   |      12
...
category_7  |  2011-05-10   |      21
category_7  |  2011-05-11   |      7
...
category_12 |  2011-05-10   |      7
category_12 |  2011-05-11   |      15

Ahora quiero que los resultados sean algo como esto (de una consulta MySQL, no manipulada por la aplicación):

CategoryId    | 2011-05-10 | 2011-05-11 | 2011-05-12 | 2011-05-13 | 2011-05-14 |
--------------------------------------------------------------------------------
category_4    |     10     |     18     |     12     |      9     |      14    |
category_7    |     21     |      7     |     16     |      14    |      13    |
categeory_12  |      7     |     15     |     11     |      19    |       8    |
--------------------------------------------------------------------------------

Hay dos advertencias para esto:

El intervalo de fechas puede aumentar o disminuir (según la consulta)

PercentChange puede ser nulo en algunos casos (digamos que category_7 / 2011-05-12 puede no tener un valor establecido)

Así que, en última instancia, no estoy muy seguro de cómo construir la parte seleccionada de la consulta para reflejar un número dinámico de columnas (sé que tiene algo que ver con CONCAT).

Edit -> Código de trabajo parcial ->

SELECT `CategoryId`,
   MAX(IF(c.`ProcessedOn` = '2011-04-20', c.`PercentChange`, NULL)) AS '2011-04-20',
   MAX(IF(c.`ProcessedOn` = '2011-04-21', c.`PercentChange`, NULL)) AS '2011-04-21',
   MAX(IF(c.`ProcessedOn` = '2011-04-22', c.`PercentChange`, NULL)) AS '2011-04-22',
   MAX(IF(c.`ProcessedOn` = '2011-04-23', c.`PercentChange`, NULL)) AS '2011-04-23',
   MAX(IF(c.`ProcessedOn` = '2011-04-24', c.`PercentChange`, NULL)) AS '2011-04-24'
  FROM `Category_Gravity` c
 WHERE c.`ProcessedOn` >= '2011-04-20'
   AND c.`ProcessedOn` <= '2011-04-24'
 GROUP BY `CategoryId`

o que necesito hacer ahora es activar la

MAX(IF(c.`ProcessedOn` = '2011-04-20', c.`PercentChange`, NULL)) AS '2011-04-20',

en algo más dinámico (a medida que cambien los rangos de fechas)

Respuestas a la pregunta(1)

Su respuesta a la pregunta