【问题标题】:SQL How to use SUM and Group By with multiple tables?SQL 如何对多个表使用 SUM 和 Group By?
【发布时间】:2017-11-21 16:09:46
【问题描述】:

我有三张桌子。

对于每个“id”值,我想分别列出 col1 值的总和、col2 值的总和和 col3 值的总和。我不是跨表求和。

table a
num  | id  |  col1
================
1     100     0
2     100     1
3     100     0

1     101     1
2     101     1
3     101     0

table b
idx  | id  | col2
=================
1     100    20
2     100    20
3     100    20

4     101   100
5     101   100

table c
idx | id | col3
==============================
1     100     1 
2     100     1
3     100     1

4     101    10
5     101     1

我希望结果看起来像这样,

ID | sum_col1 | sum_col2 | sum_col3
====================================
100      1         60          3    
101      2        200         11

这是我的查询,它运行时间过长然后超时。我的表大约有 25,000 行。

SELECT a.id as id,
SUM(a.col1) as sum_col1,
SUM(b.col2) as sum_col2,
SUM(c.col3) as sum_col3 
FROM a, b, c
WHERE a.id=b.id
AND a=id=c.id
GROUP by id 
Order by id desc

每个表中的行数可能不同,但每个表中“id”值的范围是相同的。

这似乎是一个类似的问题,但我无法使其工作,

Mysql join two tables sum, where and group by

【问题讨论】:

  • 试试Group By a.idOrder by a.id desc。您应该摆脱旧式连接并使用JOIN
  • 所有表格中是否总是包含所有 ID 号?或者是否存在表 a 的 ID 为 107 但在 b 或 c 中不存在的情况,表 b 有 199 但在 a 或 c 中不存在的情况等
  • 列“id”的值在您的示例中不是唯一的。使用非唯一键加入数据将产生笛卡尔积,这可能是导致性能问题的原因。在连接中使用 a.num = b.idx 和 b.idx = c.idx。其他选项是在子查询中分别对每个表进行 SUM 并加入结果。
  • 是的 - 所有表都有所有的 ID 号,但行数不一定相同。
  • 然后加入解决方案将起作用..直到它不起作用(而联合解决方案将始终起作用)

标签: mysql sql


【解决方案1】:

这是基于您的数据的解决方案。您的查询问题是您正在连接非唯一列上的表,从而导致笛卡尔积。

数据

DROP TABLE IF EXISTS A;

CREATE TABLE A
(num int,
 id int,
 col1 int);

 INSERT INTO A VALUES (1,     100,     0);
 INSERT INTO A VALUES (2,     100,     1);
 INSERT INTO A VALUES (3,     100,     0);
 INSERT INTO A VALUES (1,     101,     1);
 INSERT INTO A VALUES (2,     101,     1);
 INSERT INTO A VALUES (3 ,    101,     0);

DROP TABLE IF EXISTS B;

 CREATE TABLE B
(idx int,
 id int,
 col2 int);

 INSERT INTO B VALUES (1,     100,     20);
 INSERT INTO B VALUES (2,     100,     20);
 INSERT INTO B VALUES (3,     100,     20);
 INSERT INTO B VALUES (4,     101,     100);
 INSERT INTO B VALUES (5,     101,     100);

DROP TABLE IF EXISTS C;

CREATE TABLE C
(idx int,
 id int,
 col3 int);

 INSERT INTO C VALUES (1,     100,     1);
 INSERT INTO C VALUES (2,     100,     1);
 INSERT INTO C VALUES (3,     100,     1);
 INSERT INTO C VALUES (4,     101,     10);
 INSERT INTO C VALUES (5,     101,     1);

解决方案

SELECT a_sum.id, col1_sum, col2_sum, col3_sum
 FROM (SELECT id, SUM(col1) AS col1_sum
         FROM a
        GROUP BY id ) a_sum
      JOIN 
      (SELECT id, SUM(col2) AS col2_sum
         FROM b
        GROUP BY id ) b_sum 
      ON (a_sum.id = b_sum.id)
      JOIN 
      (SELECT id, SUM(col3) AS col3_sum
         FROM c
        GROUP BY id ) c_sum 
      ON (a_sum.id = c_sum.id);

结果符合预期

注意:如果 id 不必出现在所有三个表中,则进行外连接。

【讨论】:

  • 谢谢!!这行得通,而且速度很快。我什至理解它。
【解决方案2】:

这样可以吗?

还没有机会运行它,但我认为它可以完成这项工作。

SELECT sumA.id, sumA.sumCol1, sumB.sumCol2, sumC.sumCol3
FROM
(SELECT id, SUM(col1) AS sumCol1 FROM a GROUP BY id ORDER BY id ASC) AS sumA
JOIN (SELECT id, SUM(col2) AS sumCol2 FROM b GROUP BY id ORDER BY id ASC) AS sumB ON sumB.id = sumA.id
JOIN (SELECT id, SUM(col3) AS sumCol3 FROM c GROUP BY id ORDER BY id ASC) AS sumC ON sumC.id = sumB.id
;

编辑

SELECT IF(sumA.id IS NOT NULL, sumA.id, IF(sumB.id IS NOT NULL, sumB.id, IF(sumC.id IS NOT NULL, sumC.id,''))),,
sumA.sumCol1, sumB.sumCol2, sumC.sumCol3
FROM
(SELECT id, SUM(col1) AS sumCol1 FROM a GROUP BY id ORDER BY id ASC) AS sumA
OUTER JOIN (SELECT id, SUM(col2) AS sumCol2 FROM b GROUP BY id ORDER BY id ASC) AS sumB ON sumB.id = sumA.id
OUTER JOIN (SELECT id, SUM(col3) AS sumCol3 FROM c GROUP BY id ORDER BY id ASC) AS sumC ON sumC.id = sumB.id
;

【讨论】:

  • 请注意,这仅适用于每个表的每个 id 都有一个值。如果突然表 a 有一个 id 199 的值,b 有 199 但 c 没有,那么 199 将从中消失结果集
  • 是的,这就是你需要左连接的原因
  • 不,因为如果 199 在 b 和 c 但不在 a 中,它就会消失(a left join b left join c) - 实际上你需要完全外连接来满足任何没有值的表,然后你需要丑陋的连接条件,比如 a.id =coalesce(b.id,c.id)
  • 确实,union 是最宽容的方式,它不会自动影响性能;数据库读取 75,000 行,将它们转储到 1 个哈希表或 3 个都没有关系。这不是“使用联合的自动性能损失”
  • 我会将我的 JOIN 更改为 OUTER JOIN。这就是我的意思。就像这里的答案一样:link
【解决方案3】:

我会先求和,然后合并结果,然后将它们旋转:

SELECT
  id,
  MAX(CASE WHEN which = 'a' then sumof end) as sum_a,
  MAX(CASE WHEN which = 'b' then sumof end) as sum_b,
  MAX(CASE WHEN which = 'c' then sumof end) as sum_c
FROM
(
  SELECT id, sum(col1) as sumof, 'a' as which FROM a GROUP BY id
  UNION ALL
  SELECT id, sum(col2) as sumof, 'b' as which FROM b GROUP BY id
  UNION ALL
  SELECT id, sum(col3) as sumof, 'c' as which FROM c GROUP BY id
) a
GROUP BY id

你也可以联合,然后求和:

SELECT
  id,
  SUM(CASE WHEN which = 'a' then v end) as sum_a,
  SUM(CASE WHEN which = 'b' then v end) as sum_b,
  SUM(CASE WHEN which = 'c' then v end) as sum_c
FROM
(
  SELECT id, col1 as v, 'a' as which FROM a GROUP BY id
  UNION ALL
  SELECT id, col2 as v, 'b' as which FROM b GROUP BY id
  UNION ALL
  SELECT id, col3 as v, 'c' as which FROM c GROUP BY id
) a
GROUP BY id

您不能轻松地使用连接,除非所有表都具有 ID 的所有值,在这种情况下,我会说您 可以将它们作为子查询求和,然后将结果连接在一起。但如果一个您的表突然缺少其他两个表具有的 id 值,该行从您的结果中消失(除非您在 ON 子句中使用完全外连接和一些非常丑陋的合并)

在这种情况下使用 union 会给你一个更能容忍缺失值的结果集,因为它可以处理任何表中的 ID 缺失值。因此,我们将这些表合并为一个数据集,但使用一个常量来跟踪值来自哪个表,这样我们以后可以将其提取到自己的总和中

如果任何表中不存在任何 id 值,则该列的总和将为空。如果您希望它为 0,您可以将 MAX 更改为 SUM 或将 MAX 包装在 COALESCE 中

【讨论】:

  • 你真的认为 3 union 是提高性能的好方法吗?
  • 差别不大;查询必须访问所有表中的所有数据才能计算总和。这是 union 还是 join 无关紧要
  • 我试过这个,但得到“每个派生表必须有自己的别名”错误。
  • 在这里写信a) GROUP BY id -> ) a GROUP BY id。它实际上可以是任何字符; mysql 要求你做的就是给子查询(在括号中)一个名称(紧跟最后一个括号的一些字符)。为错字道歉;我会修复帖子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-19
  • 2014-11-14
  • 2021-05-01
  • 1970-01-01
  • 2012-09-18
  • 1970-01-01
  • 2020-09-03
相关资源
最近更新 更多