mysql中的考勤系统

时间:2013-03-26 13:46:17

标签: mysql view pivot

我有这三个表:

学生表:

id | name
1  | Charles
2  | Peter
3  | Mary
4  | John
5  | Mike
...

周表:

id | week | year
1  | 1    | 2012
2  | 3    | 2012
3  | 6    | 2012
4  | 8    | 2012
5  | 9    | 2012
6  | 12   | 2012
...

出勤表:

id | student | week | control
1  | 1       | 1    | P (present)
1  | 2       | 1    | A (absent)
1  | 1       | 3    | P
1  | 2       | 3    | A
1  | 3       | 9    | P

我需要的是像这样的Pivot视图,但我不知道如何在MySQL中获得这样的结构:

id | student | week-1 | week-3 | week-9
1  | Charles | P      | P      | A
2  | Peter   | A      | A      | P
3  | Mary    | P      | A      | P

1 个答案:

答案 0 :(得分:3)

MySQL没有透视功能,但您可以使用带有CASE表达式的聚合函数复制它。您的代码将类似于以下内容:

select s.id,
  s.name,
  max(case when week=1 then control else 'A' end) Week1,
  max(case when week=3 then control else 'A' end) Week3,
  max(case when week=9 then control else 'A' end) Week9
from students s
inner join attendance a
  on s.id = a.student
group by s.id, s.name

请参阅SQL Fiddle with Demo

如果您要返回未知数量的week值,则需要在预准备语句中使用动态SQL。代码将是:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(CASE WHEN week = ',
      week,
      ' THEN control else ''A'' END) AS week',
      week
    )
  ) INTO @sql
FROM weeks;

SET @sql 
  = CONCAT('SELECT s.id,
              s.name, ', @sql, ' 
            from students s
            inner join attendance a
              on s.id = a.student
            group by s.id, s.name');

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

请参阅SQL Fiddle with Demo

结果将是:

| ID |    NAME | WEEK1 | WEEK3 | WEEK6 | WEEK8 | WEEK9 | WEEK12 |
-----------------------------------------------------------------
|  1 | Charles |     P |     P |     A |     A |     A |      A |
|  2 |   Peter |     A |     A |     A |     A |     A |      A |
|  3 |    Mary |     A |     A |     A |     A |     P |      A |

注意:如果要返回表中的所有Students,无论它们是否在attendance表中有匹配的行,那么您应该使用LEFT JOIN:< / p>

select s.id,
  s.name,
  max(case when week=1 then control else 'A' end) Week1,
  max(case when week=3 then control else 'A' end) Week3,
  max(case when week=9 then control else 'A' end) Week9
from students s
left join attendance a
  on s.id = a.student
group by s.id, s.name

SQL Fiddle with Demo