{"id":107,"date":"2014-08-07T19:09:43","date_gmt":"2014-08-08T00:09:43","guid":{"rendered":"http:\/\/en.latindevelopers.com\/ivancp\/?p=107"},"modified":"2017-08-25T19:24:59","modified_gmt":"2017-08-26T00:24:59","slug":"an-approach-to-mysql-dynamic-cross-reference-query","status":"publish","type":"post","link":"http:\/\/en.latindevelopers.com\/ivancp\/2014\/an-approach-to-mysql-dynamic-cross-reference-query\/","title":{"rendered":"MySQL dynamic pivot table"},"content":{"rendered":"<p>I&#8217;m using Redmine to manage projects and bug reports, I was needing a daily report with spent time by user on each project, nothing complicated, but I wanted a cross-reference result like Excel dynamic tables. As we know, MySQL doesn&#8217;t support dynamic cross-reference queries but I make an approach:<\/p>\n<p>1. Write the main query with rows: project identifier, task subject and total hours in a date range.<\/p>\n<pre lang=\"sql\">select p.identifier, \r\n\te.issue_id, i.subject, round(sum(e.hours),2) as total_hours\r\n\tfrom redmine_nsp.time_entries e\r\n\tinner join redmine_nsp.issues i on e.issue_id = i.id\r\n\tinner join redmine_nsp.projects p on e.project_id = p.id\r\n\twhere e.spent_on between '2014-07-01' and '2014-07-07'\r\n group by p.identifier,e.issue_id;<\/pre>\n<pre lang=\"text\">\r\n+------------+----------+----------------------------+-------------+\r\n| identifier | issue_id | subject                    | total_hours |\r\n+------------+----------+----------------------------+-------------+\r\n| bg02       |     3223 | Gestion de proyecto        |        0.25 |\r\n| bg04       |     3256 | 1.1 Preparaci\u00f3n del entor  |        0.63 |\r\n| emision    |     3251 | Desarrollar la aplicaci\u00f3n  |        3.97 |\r\n| nsp00      |     3236 | Preparar propuesta para G  |        2.02 |\r\n| nsp02      |     3234 | Subida al servidor de pro  |        0.52 |\r\n| nsp02      |     3240 | Agregar funcionalidad de   |        0.55 |\r\n| nsp02      |     3241 | Revertir el Documento      |        2.80 |\r\n| nsp02      |     3242 | Agregar Filtros en las vi  |        0.72 |\r\n| nsp02      |     3243 | Reportes de Cargo de entr  |        2.35 |\r\n| nsp02      |     3254 | Control de sesiones        |        3.23 |\r\n| nsp05      |     3252 | Mantenimiento del servido  |        0.18 |\r\n| nsp05      |     3253 | Mantenimiento a redmine    |        0.53 |\r\n| nsp06      |     3203 | Elaborar el visualzador d  |        0.23 |\r\n| nsp06      |     3228 | Evitar que se tome mas de  |        0.25 |\r\n| nsp06      |     3255 | Actualizar el porcentaje   |        1.50 |\r\n| nsp08      |     3239 | Asistencia remota          |        1.38 |\r\n+------------+----------+----------------------------+-------------+\r\n<\/pre>\n<p>2. Then list the active users between dates:<\/p>\n<pre lang=\"sql\">select e.user_id, u.login\r\nfrom redmine_nsp.time_entries e\r\n\tinner join redmine_nsp.users u on e.user_id = u.id \r\nwhere e.spent_on between '2014-07-01' and '2014-07-07'\r\ngroup by e.user_id;<\/pre>\n<pre lang=\"text\">+---------+----------+\r\n| user_id | login    |\r\n+---------+----------+\r\n|       1 | user1    |\r\n|       4 | user2    |\r\n|       5 | user3    |\r\n|       6 | user4    |\r\n+---------+----------+\r\n<\/pre>\n<p>3. Now, create a stored procedure to create a dynamic SQL command with the second query as column names and values with each Redmine issue. The second query is used as cursor.<\/p>\n<pre lang=\"sql\">\r\nDELIMITER $$\r\n\r\nCREATE PROCEDURE `get_range_summary`(dDate1 date,dDate2 date)\r\nBEGIN\r\n\r\nDECLARE done INT DEFAULT 0;\r\ndeclare p_user_id int;\r\ndeclare p_sql text;\r\ndeclare p_login varchar(255);\r\ndeclare c_users cursor for\r\n\tselect e.user_id, u.login\r\n\tfrom redmine_nsp.time_entries e\r\n\t\tinner join redmine_nsp.users u on e.user_id = u.id \r\n\twhere e.spent_on between  dDate1 and dDate2 \r\n\tgroup by e.user_id;\r\nDECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;\r\n\r\nset p_sql = 'select s.codigo,s.issue_id,s.subject ';\r\nopen c_users;\r\n\r\nread_loop: LOOP\r\n\tFETCH c_users INTO p_user_id, p_login;\r\n    IF done THEN\r\n      LEAVE read_loop;\r\n    END IF;\r\n\tset p_sql = concat(p_sql,\r\n      ', (select round(sum(t.hours),2) as total\r\n\t\tfrom redmine_nsp.time_entries t\r\n\t\twhere t.spent_on between \\'',dDate1,'\\' and \\'',dDate2,'\\'  \r\n        and t.issue_id = s.issue_id\r\n\t\tand t.user_id = ',p_user_id,') as `',p_login,'` ');\r\n\r\nEND LOOP;\r\n\r\nset @sql = concat(p_sql,' ,s.total_hours from \r\n\t(select p.identifier, e.issue_id, i.subject, \r\n         round(sum(e.hours),2) as total_hours\r\n\tfrom redmine_nsp.time_entries e\r\n\tinner join redmine_nsp.issues i on e.issue_id = i.id\r\n\tinner join redmine_nsp.projects p on e.project_id = p.id\r\n\twhere e.spent_on between \\'',dDate1,'\\' and \\'',dDate2,'\\' group by p.identifier,e.issue_id) as s');\r\n\r\n\r\nclose c_users;\r\n\r\nPREPARE stmt1 FROM @sql;\r\nEXECUTE stmt1;\r\nDEALLOCATE PREPARE stmt1; \r\n\r\nEND\r\n<\/pre>\n<p>The result of <strong>call get_range_summary(&#8216;2014-07-01&#8242;,&#8217;2014-07-07&#8217;)<\/strong>:<\/p>\n<pre lang=\"text\">\r\n+---------+----------+----------------------------+-------+--------+---------+----------+-------------+\r\n| identif | issue_id | subject                    | user1 | user2  |  user3  |   user4  | total_hours |\r\n+---------+----------+----------------------------+-------+--------+---------+----------+-------------+\r\n| bg02    |       23 | Gestion de proyecto        |  NULL |   0.25 |    NULL |     NULL |        0.25 |\r\n| bg04    |       56 | 1.1 Preparaci\u00f3n del entor  |  NULL |   0.63 |    NULL |     NULL |        0.63 |\r\n| emision |       51 | Desarrollar la aplicaci\u00f3n  |  NULL |   NULL |    3.97 |     NULL |        3.97 |\r\n| nsp00   |       36 | Preparar propuesta para G  |  NULL |   2.02 |    NULL |     NULL |        2.02 |\r\n| nsp02   |       34 | Subida al servidor de pro  |  NULL |   0.52 |    NULL |     NULL |        0.52 |\r\n| nsp02   |       40 | Agregar funcionalidad de   |  NULL |   NULL |    0.55 |     NULL |        0.55 |\r\n| nsp02   |       41 | Revertir el Documento      |  1.40 |   NULL |    1.40 |     NULL |        2.80 |\r\n| nsp02   |       42 | Agregar Filtros en las vi  |  NULL |   NULL |    0.72 |     NULL |        0.72 |\r\n| nsp02   |       43 | Reportes de Cargo de entr  |  NULL |   NULL |    NULL |     2.35 |        2.35 |\r\n| nsp02   |       54 | Control de sesiones        |  NULL |   NULL |    NULL |     3.23 |        3.23 |\r\n| nsp05   |       52 | Mantenimiento del servidor |  NULL |   0.18 |    NULL |     NULL |        0.18 |\r\n| nsp05   |       53 | Mantenimiento a redmine    |  NULL |   0.53 |    NULL |     NULL |        0.53 |\r\n| nsp06   |        3 | Elaborar el visualzador de |  NULL |   0.23 |    NULL |     NULL |        0.23 |\r\n| nsp06   |       28 | Evitar que se tome mas de  |  NULL |   0.25 |    NULL |     NULL |        0.25 |\r\n| nsp06   |       55 | Actualizar el porcentaje d |  NULL |   1.50 |    NULL |     NULL |        1.50 |\r\n| nsp08   |       39 | Asistencia remota          |  NULL |   1.38 |    NULL |     NULL |        1.38 |\r\n+---------+----------+----------------------------+-------+--------+---------+----------+-------------+\r\n<\/pre>\n<p>That&#8217;s it. I put the result easily in a php based report and added it to cron task.<\/p>\n<div class=\"sharedaddy sd-sharing-enabled\"><div class=\"robots-nocontent sd-block sd-social sd-social-official sd-sharing\"><h3 class=\"sd-title\">Share this:<\/h3><div class=\"sd-content\"><ul><li class=\"share-twitter\"><a href=\"https:\/\/twitter.com\/share\" class=\"twitter-share-button\" data-url=\"http:\/\/en.latindevelopers.com\/ivancp\/2014\/an-approach-to-mysql-dynamic-cross-reference-query\/\" data-text=\"MySQL dynamic pivot table\"  >Tweet<\/a><\/li><li class=\"share-facebook\"><div class=\"fb-share-button\" data-href=\"http:\/\/en.latindevelopers.com\/ivancp\/2014\/an-approach-to-mysql-dynamic-cross-reference-query\/\" data-layout=\"button_count\"><\/div><\/li><li class=\"share-end\"><\/li><\/ul><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>I&#8217;m using Redmine to manage projects and bug reports, I was needing a daily report with spent time by user on each project, nothing complicated, but I wanted a cross-reference result like Excel dynamic tables. As we know, MySQL doesn&#8217;t support dynamic cross-reference queries but I make an approach: 1. Write the main query with [&hellip;]<\/p>\n<div class=\"sharedaddy sd-sharing-enabled\"><div class=\"robots-nocontent sd-block sd-social sd-social-official sd-sharing\"><h3 class=\"sd-title\">Share this:<\/h3><div class=\"sd-content\"><ul><li class=\"share-twitter\"><a href=\"https:\/\/twitter.com\/share\" class=\"twitter-share-button\" data-url=\"http:\/\/en.latindevelopers.com\/ivancp\/2014\/an-approach-to-mysql-dynamic-cross-reference-query\/\" data-text=\"MySQL dynamic pivot table\"  >Tweet<\/a><\/li><li class=\"share-facebook\"><div class=\"fb-share-button\" data-href=\"http:\/\/en.latindevelopers.com\/ivancp\/2014\/an-approach-to-mysql-dynamic-cross-reference-query\/\" data-layout=\"button_count\"><\/div><\/li><li class=\"share-end\"><\/li><\/ul><\/div><\/div><\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[4],"tags":[5,22,6],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p2f09V-1J","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/posts\/107"}],"collection":[{"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/comments?post=107"}],"version-history":[{"count":15,"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/posts\/107\/revisions"}],"predecessor-version":[{"id":132,"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/posts\/107\/revisions\/132"}],"wp:attachment":[{"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/media?parent=107"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/categories?post=107"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/en.latindevelopers.com\/ivancp\/wp-json\/wp\/v2\/tags?post=107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}