[英]Count of multiple items in one MySQL query returns an incorrect result
Say, if I define my tables as such:说,如果我这样定义我的表:
create table `usrs` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`fnm` TINYTEXT,
`lnm` TINYTEXT
);
insert into `usrs` (`fnm`, `lnm`)
VALUES
('John', 'Doe'), -- 1
('Mary', 'Smith'), -- 2
('Peter', 'Pan'), -- 3
('Michael', 'Jackson'); -- 4
create table `pmts` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`uid` INT UNSIGNED, -- id in `usrs` table
`amt` DOUBLE,
`flgs` INT UNSIGNED
);
insert into `pmts` (`uid`, `amt`, `flgs`)
VALUES
('3', '0.99', 0),
('1', '1.50', 0x80),
('3', '2', 0x80),
('3', '0.99', 0),
('4', '1.30', 0),
('3', '2.40', 0),
('1', '2.55', 0x80);
create table `downloads` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`uid` INT UNSIGNED, -- id in `usrs` table
`app` TINYTEXT
);
insert into `downloads` (`uid`, `app`)
VALUES
('2', 'Candy Crush'),
('2', 'Skype'),
('3', 'Word'),
('2', 'Calc'),
('4', 'Doom'),
('3', 'Notepad');
Then when I run this query, that among other things counts pmts
, downloads
and special flgs
for each usr
:然后,当我运行此查询时,除其他外, flgs
计算每个usr
pmts
、 downloads
和特殊flgs
:
-- this query contains additional clauses that were used in my production query
SELECT t1.*,
COUNT(t2.`id`) as cPmts, -- count `pmts` for each user
SUM(IF((t2.`flgs`&0x80)<>0, 1, 0)) as cPmtFlgs, -- count `pmts` with `flgs` equal to 0x80 for each user
COUNT(t3.`id`) as cDwds -- count `downloads` for each user
FROM `usrs` t1
LEFT JOIN `pmts` t2 ON t1.`id`=t2.`uid`
LEFT JOIN `downloads` t3 ON t1.`id`=t3.`uid`
WHERE t1.`id` > 0 -- just to simulate some condition
GROUP BY t1.`id`
ORDER BY t1.`fnm`, t1.`lnm` ASC
LIMIT 0, 10
the results I receive, marked in red, are wrong:我收到的结果,用红色标记,是错误的:
Why?为什么?
Your problem is that because Peter Pan
has multiple payments and multiple downloads, his rows are being duplicated in the JOIN
(you can see this if you remove the GROUP BY
and replace the aggregation functions in the query with a simple SELECT *
[demo] ).您的问题是,因为Peter Pan
有多次付款和多次下载,所以他的行在JOIN
中被复制(如果您删除GROUP BY
并用简单的SELECT *
[demo]替换查询中的聚合函数,您可以看到这一点) . The way to work around this is to perform the aggregations in derived tables and JOIN
those results instead:要解决这个问题的方法是在派生表进行聚合和JOIN
的结果,而不是:
SELECT t1.*,
COALESCE(t2.cPmts, 0) AS cPmts, -- count `pmts` for each user
COALESCE(t2.cPmtFlgs, 0) AS cPmtFlgs, -- count `pmts` with `flgs` equal to 0x80 for each user
COALESCE(t3.cDwds, 0) AS cDwds -- count `downloads` for each user
FROM `usrs` t1
LEFT JOIN (SELECT uid,
COUNT(*) AS cPmts,
SUM(IF((`flgs`&0x80)<>0, 1, 0)) AS CpmtFlgs
FROM `pmts`
GROUP BY uid) t2 ON t1.`id`=t2.`uid`
LEFT JOIN (SELECT uid, COUNT(*) AS cDwds
FROM `downloads`
GROUP BY uid) t3 ON t1.`id`=t3.`uid`
WHERE t1.`id` > 0 -- just to simulate some condition
ORDER BY t1.`fnm`, t1.`lnm` ASC
LIMIT 0, 10
Output:输出:
id fnm lnm cPmts cPmtFlgs cDwds
1 John Doe 2 2 0
2 Mary Smith 0 0 3
4 Michael Jackson 1 0 1
3 Peter Pan 4 1 2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.