2009-08-27 94 views
1

我有一个表(“转储”)的交易,我想列出的总额,按类别分组,每月,如:本月|类别|类别ID |和。涉及的表是这样的:获取对不同行的SUM()在MySQL

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_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;

,并做了查询该表,一切工作正常使用正确的总和。有没有办法在原始查询中做到这一点,与子查询或类似的东西?

+0

如果您取出SUM和GROUP BY并选择金额,您是否看到重复的记录? – 2009-08-27 19:20:37

+0

Eric:是的。我如何摆脱它们? :) – Par 2009-08-27 19:52:29

+0

如果你加入dump和dump_cat你会得到任何重复吗?如何dump_cat和类别? – 2009-08-27 20:26:30

回答

2

这样总计的3100,总和的一半以上所列。如果我从最后一个查询中删除“DISTINCT d.id”,则每行都列出两次。

虽然您可以在每个转储只有一类,因此你必须在每dump_cat转储有多个。你应该考虑定义UNIQUE约束,以确保每对did只有一个行存在,cid

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程序员的常见误解。

+0

非常感谢!我没有注意到这一点,数据导入算法中出现了错误。 现在,删除重复的SQL不起作用(查询OK,0行受到影响),是否有另一种方式来写? – Par 2009-08-27 20:38:38

+0

受影响的行不意味着它没有工作,这意味着它没有发现任何重复。所以也许我的理论认为你有重复是错误的。 – 2009-08-27 20:40:15

+0

我确实使用了“select did,cid,count(*)from dump_cat group by did,cid count(*)> 1;”。仍然删除没有帮助。但我用“create table dump_cat_unique SELECT distinct * FROM dump_cat;”来修复它然后丢弃旧的dump_cat并重命名新的。现在一切都很好,再次感谢。 – Par 2009-08-27 20:49:55

1

在第一次考试,它看起来像你对我可能有bgetween转储和Dump_Cat参照完整性约束倒退。

灿交易(在转储)是在多个类别?如果不是,那么交易表(转储)不应该指定每个交易在哪个类别,而不是更多的方式?即应该在转储表中存在CatId而不是Cat表中的DumpId?

如果交易可以在多个类别,那么你的数据结构是正确的,你butthen难免会加倍(或乘)计数交易在任何聚集查询金额,因为交易金额其实是在多个类别。

+0

我打算使用多个类别,因此使用dump_cat的数据库方案负责转储和类别之间的关系。但是我还没有,所以我查询的数据在转储中每行只有一个类别。 – Par 2009-08-27 19:58:28

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; 
+0

我可以看到你的后,但我还没有添加任何转储记录到多个类别。转储中的每条记录只有一个类别。 – Par 2009-08-27 20:01:21

+0

此外,真的会影响sum()按类别分组吗?我只能看到,它会使该月的总金额过大,但不是每个类别的总和,因为转储记录不会在类别内重复,对吗? – Par 2009-08-27 20:20:27

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 

这可能不是做你的查询最有效的方式,我可能已经得到了一些表别名错的,但应该给你如何做到这一点的想法。

+0

很高兴知道,谢谢。 – Par 2009-08-27 20:54:53