Mysql根据日期加入两个表

时间:2013-07-13 04:50:12

标签: php mysql

我在mysql数据库中有一个学生出勤视图,如下所示

id    date         grade
239   01/09/2012   1
239   02/09/2012   0
239   04/09/2012   0
239   05/09/2012   1
239   07/09/2012   0
239   25/09/2012   0
239   26/09/2012   0

我还在数据库中有一个离开申请表,如下所示。批准= 1且拒绝= 0.

id  uid    from            to           status
 1   239    04/09/2012    07/09/2012  approved
 2   239    26/09/2012    26/09/2012  rejected

现在我想创建一个查询来显示批准的假期和学生的最终成绩,如下所示

id    date       grade    leave   Fgrade
239   01/09/2012   1                 1
239   02/09/2012   0                 0
239   04/09/2012   0       1         1
239   05/09/2012   1       1         1    
239   07/09/2012   0       1         1
239   25/09/2012   0                 0
239   26/09/2012   0                 0

请告诉我如何才能得到这个结果。

2 个答案:

答案 0 :(得分:3)

已修改

(基于后面评论中提供的澄清的修改:)

SELECT a.id
     , a.date
     , a.grade
     , NULLIF(MAX(l.id) IS NOT NULL,0) AS leave
     , IF(MAX(l.id) IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to + INTERVAL 1 DAY > a.date
   AND l.status = 'Approved'
 GROUP BY a.id, a.date, a.grade

对于性能,您可能需要索引

... ON `student_attendance` (`id`, `date`, `grade`) 
... ON `leave_application` (`uid`, `status`, `from`, `to`, `uid`)

您可以使用EXPLAIN SELECT ...获取有关访问计划的信息。

8.8.1 Optimizing Queries with Explain http://dev.mysql.com/doc/refman/5.5/en/using-explain.html


<强>早些时候:

我认为这会返回指定的结果集。

SELECT a.id
     , a.date
     , a.grade
     , l.id AS leave
     , IF(l.id IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to >= a.date
   AND l.status = 1 /* approved */

JOIN谓词与学生ID相匹配,并对休假期间的出勤日期进行范围检查,以及批准的假期。

如果没有匹配(重叠)离开行,则会为fgrade分配成绩列中的值。否则,我们为fgrade分配1。

根据您的问题留下的评论,IF文件中作为IF函数中第三个参数的文字1可以替换为来自leave_application的状态列的引用。如果学生获准休假,并且还获得了成绩,则增加成绩和休假状态可能会导致比您想要的更高的价值。 1 + 1 = 2。

IF(l.id IS NULL,a.grade,l.status) AS fgrade

student_attendance中的一行可能会与leave_application表中的多行匹配。我们可以通过GROUP BY和聚合来解决这个问题......

SELECT a.id
     , a.date
     , a.grade
     , MAX(l.id) AS leave
     , IF(MAX(l.id) IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to >= a.date
   AND l.status = 1 /* approved */
 GROUP BY a.id, a.date, a.grade

如果status实际上是包含'Approved'的字符串,则可以调整查询。离开列的值可能不是id。从示例数据中无法判断,因为leave的值与id匹配,并且状态'Approved'的值为1。所以,这个值实际上可以来自

 l.status AS leave
 NULLIF(l.id IS NOT NULL,0) AS leave
 IF(l.id IS NOT NULL,1,NULL) AS leave
 (l.id/l.id) AS leave

任何这些表达式都会给出示例数据中显示的结果。


同样,根据您对问题的评论中提供的其他信息......

SELECT a.id
     , a.date
     , a.grade
     , NULLIF(MAX(l.id) IS NOT NULL,0) AS leave
     , IF(MAX(l.id) IS NULL,a.grade,1) AS fgrade
  FROM student_attendance a
  LEFT
  JOIN leave_application l 
    ON l.uid = a.id
   AND l.from <= a.date
   AND l.to >= a.date
   AND l.status = 'Approved'
 GROUP BY a.id, a.date, a.grade

答案 1 :(得分:3)

尝试

SELECT a.id, a.date, a.grade,
       CASE WHEN l.status = 'approved' THEN 1 END `leave`,
       CASE WHEN l.status = 'approved' THEN 1 ELSE a.grade END fgrade
  FROM attendance a LEFT JOIN `leave` l
    ON a.id = l.uid 
   AND a.date BETWEEN l.from AND l.to

输出:

|  ID |       DATE | GRADE |  LEAVE | FGRADE |
----------------------------------------------
| 239 | 2012-09-01 |     1 | (null) |      1 |
| 239 | 2012-09-02 |     0 | (null) |      0 |
| 239 | 2012-09-04 |     0 |      1 |      1 |
| 239 | 2012-09-05 |     1 |      1 |      1 |
| 239 | 2012-09-07 |     0 |      1 |      1 |
| 239 | 2012-09-25 |     0 | (null) |      0 |
| 239 | 2012-09-26 |     0 | (null) |      0 |

这是 SQLFiddle 演示