是否有两个LEFT JOIN用于生产笛卡尔积?

时间:2019-02-17 09:10:38

标签: mysql anti-patterns

我正在阅读 Bill Karwin 的《 SQL Antipattern》 一书。在第18章中,他讨论了有关此示例的错误的复杂查询:

SELECT p.product_id, 
COUNT(f.bug_id) AS count_fixed, 
COUNT(o.bug_id) as count_open 
FROM BugsProducts p
LEFT Outer JOIN (BugsProducts bpf JOIN Bugs f Using (bug_id))
   ON (p.bug_id = f.bug_id AND f.status = 'FIXED')
LEFT OUTER JOIN (BugsProducts bpo JOIN Bugs o Using (bug_id)) 
   ON (p.bug_id = o.bug_id AND o.status = 'OPEN')
WHERE p.product_id = 1
GROUP BY p.product_id

他声称:

  

您碰巧知道实际上有11个固定的错误,   给定产品的七个未解决的错误。所以查询的结果是   令人费解:

product_id | count_fixed | count_open 
    1      |    77       |     77

然后解释说,发生这种情况是因为结果是固定和开放错误之间的笛卡尔积。

我不明白为什么会发生这种情况,因此我使用MySQL 5.7.25重建了此查询。

结果令人惊讶

product_id | count_fixed | count_open 
    1      |    11       |     7

也可以简单地将(BugsProducts bpf JOIN Bugs f Using (bug_id))替换为Bugs f,将(BugsProducts bpf JOIN Bugs o Using (bug_id))替换为Bugs o

为什么声称该查询应做笛卡尔乘积?由于MySQL的某些特殊性在其他数据库中不起作用,因此查询是否仅返回11/7结果?

1 个答案:

答案 0 :(得分:1)

两个左联接有时会产生笛卡尔积。

在这种情况下,查询根本没有意义。很有可能是一个错误。

尝试删除GROUP BY p.product_id并将select子句更改为:

SELECT p.product_id, 
f.bug_id AS bug1Id, 
o.bug_id as bug2Id 

这样,结果集将更加明显。

我假设我们有以下表格:

  • 产品(product_id)
  • 错误(bug_id,状态)
  • BugsProducts(bug_id,product_id)

其中BugsProducts是Products和Bug之间的联接表

对于查询,它正在尝试

  • 获取product_id = 1的所有BugsProducts(p)行
  • 通过(BugsProducts-Bugs内部联接)与(p)相同的 bug_id 与1st联接,因此,如果status ='FIXED',则将BugsProducts的每一行与其自身以及Bugs表联接 / strong>,因此要总结一下,如果错误行的状态为'FIXED',则将其连接到p(本身就是p),否则不连接任何行(您看到bug1Id为NULL)
  • 与第二次连接相同,但条件状态为'OPEN'

无论如何,我相信作者想证明的东西是这样的:

SELECT p.product_id, 
COUNT(f.bug_id) AS count_fixed, 
COUNT(o.bug_id) as count_open 
FROM Products p
LEFT Outer JOIN (BugsProducts bpf JOIN Bugs f Using (bug_id))
   ON (bpf.product_id = p.product_id AND f.status = 'FIXED')
LEFT OUTER JOIN (BugsProducts bpo JOIN Bugs o Using (bug_id)) 
   ON (bpo.product_id = p.product_id AND o.status = 'OPEN')
WHERE p.product_id = 1
Group by p.product_id

产生笛卡尔积。