【问题标题】:count is multiplied after adding left join添加左连接后计数相乘
【发布时间】:2019-07-16 00:37:30
【问题描述】:

在我的查询http://www.sqlfiddle.com/#!9/16b9c2/3/0

SELECT f.name, COUNT(s.letters), AVG(t.numbers)

FROM first f

INNER JOIN second s 
ON s.first_id = f.id AND s.letters IN ('aa', 'bb', 'cc', 'dd')

LEFT JOIN third t
ON t.first_id = f.id

GROUP BY f.id

在加入 第三张桌子之前,计数是 4 - 1,但在加入第三张桌子之后,它变成了 16 - 1,这是错误的,因为它必须4 - 1

【问题讨论】:

    标签: mysql sql


    【解决方案1】:

    您的平均值也可能略有偏差——或者至少需要额外的不必要的计算。

    原因是行数沿两个维度成倍增加。更准确的解决方案是聚合 beforejoin:

    SELECT f.name, s.num_letters, AVG(t.numbers)
    FROM first f INNER JOIN
         (SELECT s.first_id, COUNT(*) as num_letters
          FROM second s 
          WHERE s.letters IN ('aa', 'bb', 'cc', 'dd')  
          GROUP BY s.first_id
         ) s
         ON s.first_id = f.id LEFT JOIN
         third t
         ON t.first_id = f.id
    GROUP BY f.id, s.num_letters;
    

    对于此类查询,最好将所有聚合移动到子查询中:

    SELECT f.name, s.num_letters, t.avg_numbers
    FROM first f INNER JOIN
         (SELECT s.first_id, COUNT(*) as num_letters
          FROM second s 
          WHERE s.letters IN ('aa', 'bb', 'cc', 'dd')  
          GROUP BY s.first_id
         ) s
         ON s.first_id = f.id LEFT JOIN
         (SELECT t.first_id, AVG(t.numbers) as avg_numbers
          FROM third t
          GROUP BY t.first_id
         ) t
         ON t.first_id = f.id;
    

    【讨论】:

    • 如果我删除第三个连接并在像f.name, COUNT(s.letters), (SELECT AVG(t.numbers) FROM third t WHERE t.first_id = f.id) 这样的子查询中计算它会更糟吗?像这里sqlfiddle.com/#!9/0080aa/2/0 这样的适当索引?
    • @JoeDoe 。 . .那将是等效的。
    【解决方案2】:

    有点难看,但得到了答案: http://www.sqlfiddle.com/#!9/16b9c2/26

    SELECT s2.name, s2.letters, AVG(t.numbers)
    
    FROM (
      select f.id, f.name as name, COUNT(s.letters) as letters
      from first f
      JOIN second s 
        ON s.first_id = f.id AND s.letters IN ('aa', 'bb', 'cc', 'dd')
      GROUP BY f.id) s2
    JOIN third t
      ON t.first_id = s2.id
    group by s2.name;
    

    在您的情况下它是左连接还是连接或内连接都没有区别,因为每个值都是可连接的。 “扇出”的发生是因为数字和字母实际上是交叉连接的,因为它们没有连接条件。

    建议的解决方案执行两个分组,通过在每个级别聚合来消除交叉。

    【讨论】:

      【解决方案3】:

      计算返回的每一行,因此从fs 的多个成功连接将被再次连接到每个成功的joino t

      要修复,请使用count(distinct ...)

      SELECT f.name, COUNT(distinct s.letters), AVG(t.numbers)
      ...
      

      【讨论】:

      • 得到 0 - 1 而不是实际的 4 - 1
      猜你喜欢
      • 1970-01-01
      • 2019-05-19
      • 2011-01-14
      • 2014-08-08
      • 2015-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-16
      相关资源
      最近更新 更多