【问题标题】:Translating an Aggregate, Outer Join Query to ANSI-92将聚合的外部连接查询转换为 ANSI-92
【发布时间】:2013-04-29 23:32:28
【问题描述】:

执行摘要:根据 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 

totakecount 列中应该有一个“8”。取而代之的是 一个零。作为一个实验,我将“RIGHT”更改为“INNER”,并且 得到了同样的结果。所以我显然做错了什么 语法,但我的网络搜索并没有告诉我什么。

谢谢。

【问题讨论】:

    标签: mysql sql aggregate-functions outer-join


    【解决方案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      |
    +----+-----------+---------+--------+-----------+-------------+-------------+--------------+
    

    【讨论】:

    • 嗯。我什至从未想过 Cross Join。谢谢,我显然需要对 ANSI-92 做更多的研究。
    【解决方案2】:

    我不确定您的原始查询是 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) 加入 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

    【讨论】:

    • 差不多。第一个给了我同样不想要的结果,第二个给了我一排空值,totakecount 为 9。但是你关于交叉连接的观点很好,谢谢。
    • @JohnMGamble 。 . .请参阅我对答案的补充。我认为,cross join 在语义上是等价的。显然,他们不是。
    猜你喜欢
    • 2018-04-18
    • 2017-04-08
    • 2014-07-08
    • 1970-01-01
    • 1970-01-01
    • 2021-06-19
    • 2010-11-15
    • 1970-01-01
    • 2013-08-01
    相关资源
    最近更新 更多