简体   繁体   English

一个 MySQL 查询中的多个项目的计数返回不正确的结果

[英]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 pmtsdownloads和特殊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

Demo on SQLFiddle SQLFiddle 上的演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM