2012-03-12 44 views
0

这是第一次 - 我试图做这样的事情 - 所以请忍受我。 这是在MySql上。MySql - Sql查询或过程,通过将行转换为列来生成报告

我正在尝试生成报告,以查看哪些学生已完成哪些主题以及哪些日期。

这是我跑

 
select u.email,t.topic_name,tu.date_created as 'date completed' 
from topic_user tu 
join topic t ON tu.topic_id = t.topic_id 
join user u ON tu.user_id = u.user_id 

它会返回结果一样

 
email   | topic_name  | date completed 
[email protected] | ABC    | 03/01/2012 
[email protected] | DEF    | 03/02/2012 
[email protected] | ABC    | 03/08/2012 
[email protected] | GHI    | 03/08/2012 
[email protected] | ABC    | 03/02/2012 
[email protected] | XYZ    | 03/10/2012 

我要生成的报告的方式,当前查询有主题名称为表头和日期他们完成它作为值

 
email   | ABC  | DEF  | GHI  | JKL  | XYZ  
[email protected] | 03/08/2012 | 03/02/2012 | 03/08/2012 | null  | null 
[email protected] | 03/02/2012 | null  | null  | null  | 03/10/2012 

几件事情要注意的是:

1)所有的主题名称将来自主题表 - 即使他们没有被学生们完成的 - 值应显示为空

2)柜面学生[email protected] - 他已经两次研究了ABC主题 - 但报告应该获得最新的日期。

我想我必须编写一个存储过程来完成此操作。就像可能首先从主题表中拉出所有主题名称,然后创建一个临时视图并填充它。

我希望你能提供任何帮助。 非常感谢

+0

主题名称是否已修复?他们可以在SQL内硬编码?您是否保证每个电子邮件的主题名称只能有一个date_completed? – 2012-03-12 15:12:08

+0

是主题名称在创建课程的意义上是固定的,主题表填充了名称。在需要时添加新的主题。没有,正如我所提到的,同一个学生可以多次研究同一个主题,但报告应该显示最后的日期。如果你看看[email protected]的学生 - 他已经完成了两次ABC课题,但报告显示了最后的日期。谢谢 – Gublooo 2012-03-12 15:44:23

回答

2

我没有测试这一点,我的经验与MySQL是有限的,但我希望下面是你在之后。它使用GROUP_CONCAT函数动态创建SELECT语句,然后执行它(这是我不确定在MySQL中执行它的方式)。

SET @SQL = (
SELECT CONCAT('SELECT Email,', GROUP_CONCAT(SelectText), ' FROM Topic_User tu INNER JOIN Users u ON u.User_ID = tu.User_ID GROUP BY Email') 
FROM ( SELECT CONCAT(' MAX(CASE WHEN Topic_ID = ', Topic_ID, ' THEN tu.Date_Created END) AS `', Topic_Name, '`') AS SelectText 
      FROM Topic 
     ) AS d); 

PREPARE stmt FROM @SQL; 
EXECUTE stmt 

当然,如果你的主题不是很经常改变你可以只使用:

SELECT Email, 
     MAX(CASE WHEN Topic_ID = 1 THEN tu.Date_Created END) AS ABC, 
     MAX(CASE WHEN Topic_ID = 2 THEN tu.Date_Created END) AS DEF, 
     MAX(CASE WHEN Topic_ID = 3 THEN tu.Date_Created END) AS GHI, 
     MAX(CASE WHEN Topic_ID = 4 THEN tu.Date_Created END) AS JKL, 
     MAX(CASE WHEN Topic_ID = 5 THEN tu.Date_Created END) AS XYZ 
FROM Topic_User tu 
     INNER JOIN users u 
      ON u.User_ID = tu.User_ID 
GROUP BY Email 

并且每次添加一个新的话题时改变查询(这是上面的过程中产生的查询)。

+0

很好的解决方案。它应该工作正常,只要它在MySQL> = 5.0.13 – nnichols 2012-03-12 16:20:12

+0

谢谢 - 我喜欢解决方案,当主题不经常更改 - 但数据库有大约90个奇怪的主题,我将不得不添加所有这些情况。你的第一个动态创建解决方案是理想的,但我无法让它工作 - 我收到错误 - “你的SQL语法有错误;检查与你的MySQL服务器版本对应的手册正确的语法使用附近'银行,MAX(CASE当时topic_id = 3 THEN tu.date_created结束)AS T'第1行“ – Gublooo 2012-03-12 17:07:58

+0

我期望这是一个'topic_name'与一个空间。编辑解决方案来解决这个问题。如果不是这样,请在运行之前选择@SQL,这会向您显示已生成的查询,并应帮助您确定发生错误的位置。 – GarethD 2012-03-12 17:22:43

1

您可以使用动态生成的交叉表查询来做到这一点。你想结束了查询是这样的 -

SELECT 
    u.email, 
    MAX(IF(t.topic_name = 'ABC', tu.date_created, NULL)) AS 'ABC', 
    MAX(IF(t.topic_name = 'DEF', tu.date_created, NULL)) AS 'DEF', 
    etc 
FROM topic_user tu 
INNER JOIN topic t 
    ON tu.topic_id = t.topic_id 
INNER JOIN user u 
    ON tu.user_id = u.user_id 
GROUP BY u.user_id; 

所以,在选择,你会基于对主题列表第一查询动态生成字段列表中的服务器端语言。

你可以通过使用topic_ids代替topic_names稍微降低此查询的开销,这样你可以删除加入到主题表 -

SELECT 
    u.email, 
    MAX(IF(tu.topic_id = 1, tu.date_created, NULL)) AS 'ABC', 
    MAX(IF(tu.topic_id = 2, tu.date_created, NULL)) AS 'DEF', 
    etc 
FROM topic_user tu 
INNER JOIN user u 
    ON tu.user_id = u.user_id 
GROUP BY u.user_id; 
+0

感谢nichols - 我看到是否有任何方法可以避免在查询中列出90多个主题名称,并且如果有任何动态将它们从主题表中删除 – Gublooo 2012-03-12 17:11:51

+0

@Gublooo的方法与准备好的语句肯定更好。 – nnichols 2012-03-12 17:27:46

相关问题