【问题标题】:Understanding use of multiple SUMs with LEFT JOINS in mysql了解在 mysql 中通过 LEFT JOINS 使用多个 SUM
【发布时间】:2020-04-05 16:29:05
【问题描述】:

使用 GROUP BY 命令,可以 LEFT JOIN 多个表,并且仍然可以从第一个表中获取所需的行数。

例如,

SELECT b.title 
FROM books `b`
LEFT JOIN orders `o` 
ON o.bookid = b.id 
LEFT JOIN authors `a` 
ON b.authorid = a.id 
GROUP BY b.id

但是,由于 MYSQL 在幕后对表进行笛卡尔积,如果包含多个 SUM 命令,则基于所有隐藏行会得到不正确的值。 (这里的问题是explained 相当不错。)

SELECT b.title,SUM(o.id) as sales,SUM(a.id) as authors    
FROM books `b`
LEFT JOIN orders `o` 
ON o.bookid = b.id 
LEFT JOIN authors `a` 
ON b.authorid = a.id 
GROUP BY b.id

在 SO 上有很多 answers 与此有关,大多数在 JOINS 中使用子查询,但我无法将它们应用于这个相当简单的案例。

如何调整以上内容以获得正确的 SUM?

编辑

例子

books
id|title|authorid
1|Huck Finn|1
2|Tom Sawyer|1
3|Python Cookbook|2

orders
id|bookid
1|1
2|1
3|2
4|2
5|3
6|3

authors
id|author
1|Twain
2|Beazley
2|Jones

Python Cookbook 的作者总数 # 的“正确答案”是 2。但是,因为有两个连接,并且整个数据集通过连接订单数进行扩展,所以 SUM(a.id) 将为 4 .

【问题讨论】:

  • 您能否详细说明“...正确的 SUM”?请添加一些示例数据和预期结果。
  • 见上面的例子
  • 当它不是表的 ID 时,将其称为 authors.id 真是个坏主意。这是一个糟糕的数据模型。您应该有一个包含唯一作者的作者表和一个桥接表来表示书籍和作者之间的 m:n 关系。
  • 请在代码问题中给出minimal reproducible example--cut & paste & runnable code,包括最小的代表性示例输入作为代码;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。给出您可以给出的最少代码,即您显示的代码可以通过您显示的代码扩展为不正常。 (调试基础。)对于包含 DBMS 和 DDL(包括约束和索引)和输入为格式化为表的代码的 SQL。请不要发布无效代码作为描述,它没有任何意义。用文字将输出解释为输入的函数。
  • 这似乎是一个常见错误,人们想要一些连接,每个可能涉及不同的键,一些子查询,每个可能涉及连接和/或聚合,但他们错误地尝试执行所有然后加入所有聚合或聚合以前的聚合。在适当的行上写单独的总和和/或总结一个案例语句选择行;加入常见的唯一列集。了解 LEFT JOIN ON 返回什么: INNER JOIN ON rows UNION ALL 不匹配的左表行,由 NULL 扩展。作为 OUTER JOIN ON 的一部分,始终知道您想要什么 INNER JOIN ON。

标签: mysql sum left-join


【解决方案1】:

您是正确的,通过加入多个表您不会获得预期的结果。
但在这种情况下,您应该使用 COUNT() 而不是 SUM() 并计算不同的订单或作者。
同样根据您的设计,您应该计算作者的姓名,而不是 authors 表的 ids:

SELECT b.title, 
  COUNT(DISTINCT o.id) as sales,
  COUNT(DISTINCT a.author) as authors    
FROM books `b`
LEFT JOIN orders `o` ON o.bookid = b.id 
LEFT JOIN authors `a` ON b.authorid = a.id 
GROUP BY b.id, b.title

请参阅demo
结果:

| title           | sales | authors |
| --------------- | ----- | ------- |
| Huck Finn       | 2     | 1       |
| Tom Sawyer      | 2     | 1       |
| Python Cookbook | 2     | 2       |

【讨论】:

  • 关于 Count 的观点很好。
  • 只希望大家不要实际使用SUM()和a.id来计数。
【解决方案2】:

在处理单独的聚合时,最好在加入之前进行聚合。

您的数据模型非常混乱,使它看起来像是一本书仅由一位作者撰写(由books.authorid 引用),而这个“ID”根本不是作者的 ID。

你的主要问题是:你不算数!我们用COUNT算数。但是您错误地将 ID 值与SUM 相加。

这是一个正确的查询,我在加入之前进行聚合并使用别名来消除混淆,从而提高查询的可读性和可维护性。

SELECT
  b.title,
  COALESCE(o.order_count, 0) AS sales,
  COALESCE(a.author_count, 0) AS authors
FROM (SELECT title, id AS book_id, authorid AS author_group_id FROM books) b
LEFT JOIN
(
  SELECT id as author_group_id, COUNT(*) as author_count
  FROM authors
  GROUP BY id
) a ON a.author_group_id = b.author_group_id
LEFT JOIN
(
  SELECT bookid AS book_id, COUNT(*) as order_count
  FROM orders
  GROUP BY bookid
) o ON o.book_id = b.book_id
ORDER BY b.title;

【讨论】:

    【解决方案3】:

    我认为您的查询不会像您预期的那样工作。

    假设一本书可能有 3 个作者。

    对于作者

    因此,您的 books 表中有该书的 三行,每一行对应于每个作者。

    所以一个

    SUM(b.authorid) 
    

    在你的情况下给你正确的答案。

    对于订单

    你必须使用类似的子选择

    LEFT JOIN (SELECT SUM(id) o_sum,bookid  FROM orders GROUP BY bookid) `o` 
    ON o.bookid = b.id 
    

    你真的应该重新考虑你对书籍和作者的态度。

    【讨论】:

    • 谢谢。这样就完成了。
    • @zztop:但这是唯一错误的答案。您唯一的错误是您使用的是SUM 而不是COUNT
    • 对不起,Thorsten,直到我已经将其标记为正确并使其工作并刷新页面后,我才看到您的答案。你是对的,鉴于我在 SUM 上面使用的示例数据不起作用。在实际数据中还有很多其他列,包括我总结的数量。
    • @zztop:这与我的回答无关。 forpas 更早地发布了他们的答案,告诉您您错误地使用了SUM,并且处理重复项的方法是不同的计数。但是,好吧,因此您的示例是错误的,实际上您正在总结其他列。当尝试简化对 stackoverflow 的查询时,很容易发生这种情况:-)
    猜你喜欢
    • 1970-01-01
    • 2012-01-05
    • 2016-07-23
    • 1970-01-01
    • 2012-11-23
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多