在mysql中的不同行上获取SUM()

时间:2009-08-27 19:10:01

标签: sql mysql

我有一个包含交易的表(“转储”),我想列出每个月按类别分组的总金额,如:月|类别|类别ID |和。涉及的表格如下:

TABLE dump:
id INT
date DATE
event VARCHAR(100)
amount DECIMAL(10, 2)
TABLE dump_cat:
id INT
did INT (id in dump)
cid INT (id in categories)
TABLE categories:
id INT
name VARCHAR(100)

现在我尝试使用的查询是:

SELECT SUBSTR(d.date,1,7) AS month, c.name, c.id AS catid, SUM(d.amount) AS sum
 FROM dump as d, dump_cat as dc, categories AS c
 WHERE dc.did = d.id AND c.id = dc.cid AND SUBSTR(d.date, 1, 7) >= '2008-08'
 GROUP BY month, c.name ORDER BY month;

但大多数类别的总和是应有的两倍。我的猜测是,这是因为连接返回多行,但在字段部分中添加“DISTINCT d.id”没有任何区别。查询返回的示例如下:

+---------+--------------------------+-------+-----------+
| month   | name                     | catid | sum       |
+---------+--------------------------+-------+-----------+
| 2008-08 | Cash                     |    21 |  -6200.00 | 
| 2008-08 | Gas                      |     8 |  -2936.19 | 
| 2008-08 | Rent                     |     1 | -15682.00 | 

其中

SELECT DISTINCT d.id, d.amount FROM dump AS d, dump_cat AS dc
 WHERE d.id = dc.did AND SUBSTR(d.date, 1, 7) ='2008-08' AND dc.cid = 21;

返回

+------+----------+
| id   | amount   |
+------+----------+
| 3961 |  -600.00 | 
| 2976 |  -200.00 | 
| 2967 |  -400.00 | 
| 2964 |  -200.00 | 
| 2957 |  -300.00 | 
| 2962 | -1400.00 | 
+------+----------+

这总计3100,是上面列出的一半。如果我从上一个查询中删除“DISTINCT d.id”,则每行列出两次。我认为这是问题,但我需要帮助来弄清楚如何解决它。提前谢谢。

补充:如果我使用

将dump和dump_cat表收集到一个表中
CREATE table dumpwithcat SELECT DISTINCT d.id, d.date, d.event, d.amount, dc.cid
  FROM dump AS d, dump_cat AS c WHERE c.did = d.id;

并在该表上执行查询,一切正常,总和正常。有没有办法在原始查询中执行此操作,使用子查询或类似的东西?

4 个答案:

答案 0 :(得分:2)

  

这总计3100,是上面列出的一半。如果我从上一个查询中删除“DISTINCT d.id”,则每行列出两次。

虽然每个转储可能只有一个类别,但每个转储中dump_cat必须有多个。您应该考虑定义UNIQUE约束,以确保每对didcid只存在一行:

ALTER TABLE dump_cat ADD CONSTRAINT UNIQUE (did, cid);

我预测,如果您的表中有当前数据,此语句将失败。当这些列已包含重复项时,它无法创建唯一约束!

您可以通过这种方式删除重复项,例如:

DELETE dc1 FROM dump_cat dc1 JOIN dump_cat dc2 USING (did, cid)
WHERE dc1.id > dc2.id; -- only delete the second duplicate entry

编辑顺便说一句,在您确认我是正确的之前,请不要接受我的问题! : - )

您可以使用如下所示的查询验证实际上是否存在重复:

SELECT did, COUNT(*)
FROM dump_cat
GROUP BY did
HAVING COUNT(*) > 1;

另一种可能性:您有多个具有相同名称的类别? (对不起,我第一次尝试这个查询是错误的,这是一个编辑版本)

SELECT c.name, GROUP_CONCAT(c.id) AS cat_id_list, COUNT(*) AS c
FROM category c
GROUP BY c.name
HAVING COUNT(*) > 1;

FWIW,我测试了我展示的DELETE命令:

INSERT INTO dump_cat (did, cid) VALUES (1, 2), (3,4), (3,4); -- duplicates!

DELETE dc1 FROM dump_cat dc1 JOIN dump_cat dc2 USING (did, cid) WHERE dc1.id > dc2.id
Query OK, 1 row affected (0.00 sec)

PS:这与您的问题相关,但DISTINCT查询修饰符始终适用于整行,而不仅仅是第一列。这是许多SQL程序员的常见误解。

答案 1 :(得分:1)

在第一次检查时,我认为你可能会向后转发Dump和Dump_Cat之间的参照完整性约束。

交易(转储)可以分为多个类别吗?如果没有,那么交易表,(转储)不应该指定每个交易所在的类别,而不是otjher方式?即,转储表中是否有CatId而Cat表中是否有DumpId?

如果交易可以处于多个类别,那么您的数据结构是正确的,但是在任何聚合查询中您都不可避免地会对事务金额进行双倍(或多次)计数,因为交易金额实际上是在多个类别。

答案 2 :(得分:1)

如果转储记录可以分为多个类别,则会影响该月类别行的所有

对此的一个解决方案是为每个转储记录提取COUNT()类别,并将其用作单个金额的除数。因此,金额在转储记录所属的所有类别中以均匀的方式自动分配,从而保持整体总体的完整性。

这样的事情(对不起,MySQL不是我的日常RDBMS,不确定确切的语法):

 SELECT SUBSTR(d.date,1,7) AS month, c.name, c.id AS catid, 
   SUM(d.amount / (SELECT COUNT(*) FROM dump_cat dc2 WHERE dc2.did=d.id)) AS sum
 FROM dump as d, dump_cat as dc, categories AS c
 WHERE dc.did = d.id AND c.id = dc.cid AND SUBSTR(d.date, 1, 7) >= '2008-08'
 GROUP BY month, c.name ORDER BY month;

答案 3 :(得分:1)

您可以接受任何查询,例如您用于创建不同表的查询,然后只选择该查询。只需给查询一个“表名”。

SELECT SUBSTR(d_dc.date,1,7) AS month, c.name, c.id AS catid, SUM(d_dc.amount) AS sum
FROM (SELECT DISTINCT d.id, d.date, d.event, d.amount, dc.cid
    FROM dump AS d, dump_cat AS dc WHERE dc.did = d.id
    WHERE SUBSTR(d.date, 1, 7) >= '2008-08') AS d_dc
JOIN categories AS c ON d_dc.cid=c.id
GROUP BY month, c.name ORDER BY month

这可能不是最有效的查询方式,而且我可能已经把一些表别名弄错了,但这应该让你知道如何去做。