将聚合,外部联接查询转换为ANSI-92

时间:2013-04-29 23:32:29

标签: mysql sql aggregate-functions outer-join

执行摘要:在SQL ANSI-89下编写的查询 需要转移到使用的数据库 SQL ANSI-92。涉及外部联接。


我一直在运行ANSI-89查询

如果需要,我可以给表结构,但基本的 我们的想法是,我们有一张员工表,一张表 在线课程,以及中级"员工课程" 包含员工ID,课程ID和评级的行的表 我们用作通过/失败指示符的列。

我们关键列的表格样式是主键 始终' id',而外键总是' tablename _id'。

这里是选择的ANSI-89(MySQL 4.1.14)版本:

SELECT E.id,
   E.firstname, E.surname, E.suffix,
   sum( if( ECT.rating =1, 1, 0 ) ) AS passcount,
   sum( if( ECT.rating =0, 1, 0 ) ) AS retakecount,
   sum( if( ECT.rating IS NULL , 1, 0 ) ) AS totakecount,
   L.locationname
FROM employee E,
   location L
INNER JOIN emplcoursetaken ECT ON E.id = ECT.employee_id
RIGHT JOIN courses C ON C.id = ECT.course_id
WHERE C.linkready =1
AND E.location_id = L.id
GROUP BY E.id
ORDER BY L.locationname, E.surname, E.firstname;

它有效。我得到了一组很好的行,如下所示:

620 Johnny  Test        12  0   14  Chicago
约翰尼在26门课程中占了12门,并且超过了这12门课程。

但我在将此查询移至ANSI-92(MySQL 5.1.55)时遇到问题。

SELECT E.id,
   E.firstname, E.surname, E.suffix,
   sum( if( ECT.rating =1, 1, 0 ) ) AS passcount,
   sum( if( ECT.rating =0, 1, 0 ) ) AS retakecount,
   sum( if( ECT.rating IS NULL , 1, 0 ) ) AS totakecount,
   L.locationname
FROM employee E
INNER JOIN emplcoursetaken ECT ON E.id = ECT.employee_id
RIGHT JOIN courses C ON C.id = ECT.course_id
INNER JOIN location L ON E.location_id = L.id
WHERE C.linkready =1
GROUP BY E.id
ORDER BY L.locationname, E.surname, E.firstname;

这个数据库的课程表只有九门课程,所以 我应该得到一个不同的非零" totakecount"值:

620 Johnny  Test    NULL    1   0   0   Chicago 

应该有一个' 8'在totakecount列中。而不是那里 零。作为一项实验,我改变了" RIGHT"到" INNER"和 得到了相同的结果。所以我明显做错了 语法,但我的网络搜索并没有告诉我什么。

感谢。

2 个答案:

答案 0 :(得分:1)

在MySQL 5.5.30上测试:

SELECT E.id,
   E.firstname, E.surname, E.suffix,
   sum( if( ECT.rating =1, 1, 0 ) ) AS passcount,
   sum( if( ECT.rating =0, 1, 0 ) ) AS retakecount,
   sum( if( ECT.rating IS NULL , 1, 0 ) ) AS totakecount,
   L.locationname
FROM
courses C 
CROSS JOIN employee E
INNER JOIN location L ON E.location_id = L.id
LEFT JOIN emplcoursetaken ECT ON E.id = ECT.employee_id AND C.id = ECT.course_id
WHERE C.linkready =1
GROUP BY E.id
ORDER BY L.locationname, E.surname, E.firstname;

+----+-----------+---------+--------+-----------+-------------+-------------+--------------+
| id | firstname | surname | suffix | passcount | retakecount | totakecount | locationname |
+----+-----------+---------+--------+-----------+-------------+-------------+--------------+
|  1 | Johnny    | Test    | NULL   |         8 |           0 |           1 | Chicago      |
+----+-----------+---------+--------+-----------+-------------+-------------+--------------+

答案 1 :(得分:0)

我不确定您的原始查询是否为ANSI 89.但是,它使用,作为cross join。只需用交叉连接替换它:

SELECT E.id,
   E.firstname, E.surname, E.suffix,
   sum( if( ECT.rating =1, 1, 0 ) ) AS passcount,
   sum( if( ECT.rating =0, 1, 0 ) ) AS retakecount,
   sum( if( ECT.rating IS NULL , 1, 0 ) ) AS totakecount,
   L.locationname
FROM employee E cross join
   location L
INNER JOIN emplcoursetaken ECT ON E.id = ECT.employee_id
RIGHT JOIN courses C ON C.id = ECT.course_id
WHERE C.linkready =1
AND E.location_id = L.id
GROUP BY E.id
ORDER BY L.locationname, E.surname, E.firstname;

从外观上看,您可以将交叉连接转换为内连接:

SELECT E.id,
   E.firstname, E.surname, E.suffix,
   sum( if( ECT.rating =1, 1, 0 ) ) AS passcount,
   sum( if( ECT.rating =0, 1, 0 ) ) AS retakecount,
   sum( if( ECT.rating IS NULL , 1, 0 ) ) AS totakecount,
   L.locationname
FROM employee E join
   location L
   on E.location_id = L.id
INNER JOIN emplcoursetaken ECT ON E.id = ECT.employee_id
RIGHT JOIN courses C ON C.id = ECT.course_id
WHERE C.linkready =1
GROUP BY E.id
ORDER BY L.locationname, E.surname, E.firstname;

你的评论很有意思。我认为cross join,在语义上是相同的。这在历史上是正确的,但现在不是(如here所述):

  

以前,逗号运算符(,)和JOIN都有相同的含义   优先级,因此连接表达式t1,t2 JOIN t3被解释为   ((t1,t2)JOIN t3)。现在JOIN具有更高的优先级,所以表达式   被解释为(t1,(t2 JOIN t3))。此更改会影响语句   使用ON子句,因为该子句只能引用列   在连接的操作数中,优先级的变化会发生变化   解释这些操作数是什么。

如果是这种情况,那么括号应该解决优先级问题:

FROM (employee E cross join
      location L
     ) el
    INNER JOIN emplcoursetaken ECT ON El.id = ECT.employee_id

唯一的问题是别名问题。 。 。您可能需要将其更改为el