Consultas de referencias cruzadas en MySQL

Hace algún tiempo que uso Redmine para gestionar proyectos, la herramienta en general es buena pero a veces necesitaba de reportes diarios de tiempo utilizado por los miembros del proyecto.

En este punto se me ocurrió si podría lograr una consulta de referencias cruzadas (al estilo MS Excel), MySQL no soporta consultas de ese tipo pero pueden simularse:

1. Escribir la consulta principal que contenga las filas más importantes: project identifier, task subject y total_hours, todo en un rango de fechas.

select p.identifier, 
	e.issue_id, i.subject, round(sum(e.hours),2) as total_hours
	from redmine_nsp.time_entries e
	inner join redmine_nsp.issues i on e.issue_id = i.id
	inner join redmine_nsp.projects p on e.project_id = p.id
	where e.spent_on between '2014-07-01' and '2014-07-07'
 group by p.identifier,e.issue_id;

Resultado:

+------------+----------+----------------------------+-------------+
| identifier | issue_id | subject                    | total_hours |
+------------+----------+----------------------------+-------------+
| bg02       |     3223 | Gestion de proyecto        |        0.25 |
| bg04       |     3256 | 1.1 Preparación del entor  |        0.63 |
| emision    |     3251 | Desarrollar la aplicación  |        3.97 |
| nsp00      |     3236 | Preparar propuesta para G  |        2.02 |
| nsp02      |     3234 | Subida al servidor de pro  |        0.52 |
| nsp02      |     3240 | Agregar funcionalidad de   |        0.55 |
| nsp02      |     3241 | Revertir el Documento      |        2.80 |
| nsp02      |     3242 | Agregar Filtros en las vi  |        0.72 |
| nsp02      |     3243 | Reportes de Cargo de entr  |        2.35 |
| nsp02      |     3254 | Control de sesiones        |        3.23 |
| nsp05      |     3252 | Mantenimiento del servido  |        0.18 |
| nsp05      |     3253 | Mantenimiento a redmine    |        0.53 |
| nsp06      |     3203 | Elaborar el visualzador d  |        0.23 |
| nsp06      |     3228 | Evitar que se tome mas de  |        0.25 |
| nsp06      |     3255 | Actualizar el porcentaje   |        1.50 |
| nsp08      |     3239 | Asistencia remota          |        1.38 |
+------------+----------+----------------------------+-------------+

2. Ahora queda averiguar quienes han estado activos en ese mismo rango de tiempo:

select e.user_id, u.login
from redmine_nsp.time_entries e
	inner join redmine_nsp.users u on e.user_id = u.id 
where e.spent_on between '2014-07-01' and '2014-07-07'
group by e.user_id;
+---------+----------+
| user_id | login    |
+---------+----------+
|       1 | user1    |
|       4 | user2    |
|       5 | user3    |
|       6 | user4    |
+---------+----------+

3. La tarea es insertar ese resultado como columnas del primer resultado, de tal modo que tengamos una consulta de referencias cruzadas. He creado un procedimiento (no explico mucho al respecto) que hace esa tarea recibiendo como parámetros las fechas:

DELIMITER $$

CREATE PROCEDURE `get_range_summary`(dDate1 date,dDate2 date)
BEGIN

DECLARE done INT DEFAULT 0;
declare p_user_id int;
declare p_sql text;
declare p_login varchar(255);
declare c_users cursor for
	select e.user_id, u.login
	from redmine_nsp.time_entries e
		inner join redmine_nsp.users u on e.user_id = u.id 
	where e.spent_on between  dDate1 and dDate2 
	group by e.user_id;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;

set p_sql = 'select s.codigo,s.issue_id,s.subject ';
open c_users;

read_loop: LOOP
	FETCH c_users INTO p_user_id, p_login;
    IF done THEN
      LEAVE read_loop;
    END IF;
	set p_sql = concat(p_sql,
      ', (select round(sum(t.hours),2) as total
		from redmine_nsp.time_entries t
		where t.spent_on between \'',dDate1,'\' and \'',dDate2,'\'  
        and t.issue_id = s.issue_id
		and t.user_id = ',p_user_id,') as `',p_login,'` ');

END LOOP;

set @sql = concat(p_sql,' ,s.total_hours from 
	(select p.identifier, e.issue_id, i.subject, 
         round(sum(e.hours),2) as total_hours
	from redmine_nsp.time_entries e
	inner join redmine_nsp.issues i on e.issue_id = i.id
	inner join redmine_nsp.projects p on e.project_id = p.id
	where e.spent_on between \'',dDate1,'\' and \'',dDate2,'\' group by p.identifier,e.issue_id) as s');


close c_users;

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1; 

END

Al probar con la llamada: call get_range_summary(‘2014-07-01′,’2014-07-07’):

+---------+----------+----------------------------+-------+--------+---------+----------+-------------+
| identif | issue_id | subject                    | user1 | user2  |  user3  |   user4  | total_hours |
+---------+----------+----------------------------+-------+--------+---------+----------+-------------+
| bg02    |       23 | Gestion de proyecto        |  NULL |   0.25 |    NULL |     NULL |        0.25 |
| bg04    |       56 | 1.1 Preparación del entor  |  NULL |   0.63 |    NULL |     NULL |        0.63 |
| emision |       51 | Desarrollar la aplicación  |  NULL |   NULL |    3.97 |     NULL |        3.97 |
| nsp00   |       36 | Preparar propuesta para G  |  NULL |   2.02 |    NULL |     NULL |        2.02 |
| nsp02   |       34 | Subida al servidor de pro  |  NULL |   0.52 |    NULL |     NULL |        0.52 |
| nsp02   |       40 | Agregar funcionalidad de   |  NULL |   NULL |    0.55 |     NULL |        0.55 |
| nsp02   |       41 | Revertir el Documento      |  1.40 |   NULL |    1.40 |     NULL |        2.80 |
| nsp02   |       42 | Agregar Filtros en las vi  |  NULL |   NULL |    0.72 |     NULL |        0.72 |
| nsp02   |       43 | Reportes de Cargo de entr  |  NULL |   NULL |    NULL |     2.35 |        2.35 |
| nsp02   |       54 | Control de sesiones        |  NULL |   NULL |    NULL |     3.23 |        3.23 |
| nsp05   |       52 | Mantenimiento del servidor |  NULL |   0.18 |    NULL |     NULL |        0.18 |
| nsp05   |       53 | Mantenimiento a redmine    |  NULL |   0.53 |    NULL |     NULL |        0.53 |
| nsp06   |        3 | Elaborar el visualzador de |  NULL |   0.23 |    NULL |     NULL |        0.23 |
| nsp06   |       28 | Evitar que se tome mas de  |  NULL |   0.25 |    NULL |     NULL |        0.25 |
| nsp06   |       55 | Actualizar el porcentaje d |  NULL |   1.50 |    NULL |     NULL |        1.50 |
| nsp08   |       39 | Asistencia remota          |  NULL |   1.38 |    NULL |     NULL |        1.38 |
+---------+----------+----------------------------+-------+--------+---------+----------+-------------+

Es todo! Luego con ese resultado es fácil crear un pequeño reporte diario/semanal sobre las horas utilizadas por cada usuario.