sql计数:异常行为

xmakbtuz  于 2021-07-24  发布在  Java
关注(0)|答案(3)|浏览(365)

我编写的一段sql没有按预期的方式运行。一个重要的逻辑是计算有多少客人是VIP,但是sql似乎总是得到一个错误的答案。
以下数据库有6位访客,其中3位是VIP。

CREATE TABLE `guest` (
  `GuestID` int(11) NOT NULL DEFAULT '0',
  `fullname` varchar(255) DEFAULT NULL,
  `vip` tinyint(1) DEFAULT '0',
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `guest`
--

INSERT INTO `guest` (`GuestID`, `fullname`, `vip`) VALUES
(912, 'Sam',  0),
(321, 'Sev', 0),
(629, 'Joe', 0),
(103, 'Tom', 1),
(331, 'Cao', 1),
(526, 'Conor', 1);

最初,sql返回一个值,表示有5个vip,这是不正确的,因为只有3个vip。这是一个相当复杂的数据库,在为这个问题生成一个最小可行的示例时(有一个可重复的错误),脚本现在声明只有2个vip。同样,这是不正确的。
所讨论的sql是

SELECT slotguest.FK_SlotNo, Count(CASE WHEN guest.vip = 1 THEN 1 END) AS guest_count 
FROM guest 
INNER JOIN slotguest ON guest.GuestID = slotguest.FK_guest 
GROUP BY slotguest.FK_SlotNo;

最慢的结构和内容如下

CREATE TABLE `slotguest` (
  `FK_SlotNo` int(11) NOT NULL,
  `FK_guest` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `slotguest`
--

INSERT INTO `slotguest` (`FK_SlotNo`, `FK_guest`) VALUES
(396, 912),
(396, 321),
(396, 629),
(396, 103),
(396, 331),
(396, 526);

是什么导致count得出一个一贯错误的答案?

8wigbo56

8wigbo561#

如注解中所示(从users@f处检查ábio amorim,@rajat),您的查询似乎按预期运行。因为,你用 CASE WHEN ,使用 SUM .
如果您将不同vip类别的计数带到可能存在数据泄漏的位置,可能会更加明显。

SELECT guest.vip, slotguest.FK_SlotNo, COUNT(*) AS guest_per_category
FROM guest 
INNER JOIN slotguest ON guest.GuestID = slotguest.FK_guest
GROUP BY guest.vip,slotguest.FK_SlotNo;
643ylb08

643ylb082#

闻起来像“爆炸-内爆”。鉴于

SELECT ... COUNT(*)
    FROM a JOIN b ...
    GROUP BY ...

查询是这样执行的: JOIN table。假设表的比例不是1:1,这将导致比任何一个表都多的行。
不使用骨料(例如 COUNT )靠着那张临时table。
只有到那时 GROUP BY 收缩回最初所需的行数。
解决方案是避免对包含被计数/求和数据的多个表进行聚合。有时这种模式是

SELECT ...
     FROM ( SELECT x, COUNT(*) AS ct FROM a GROUP BY x ) AS b
     JOIN c ON ...
drnojrws

drnojrws3#

为了解释问题所在,并给出一个更接近o.p.询问的答案。。。
(我假设o.p.是出错的一个简化示例,而实际的查询更为复杂。如果我们知道大局,我想我不会那样编码。)
在o.p.查询中, CASE WHEN guest.vip = 1 THEN 1 END 格式不正确。这是一个条件表达式;它应该为查询检索到的所有行返回一个特定值,即 guest.vip <> 1 .
事实上,行为是没有定义的;它在一些数据库管理系统上产生预期的答案,正如注解所说明的那样;按照o.p.的说法,它不会影响其他人。我猜对于那些产生预期答案的人来说,dbms是在处理 CASE 作为回报 Null ,然后 Count( ) 正在忽略空值。这是一个更可怕的后果 Null 在sql中。
所以根据@fábio amorim的评论 CASE 需要一个 ELSE 因此 Count( ) 给出了一个无用的结果,所以得到 ELSE 返回 0 以及 Sum( ) 这个 1 或者 0 :

SELECT slotguest.FK_SlotNo, Sum(CASE WHEN guest.vip = 1 THEN 1 ELSE 0 END) AS guest_count 
FROM guest 
INNER JOIN slotguest ON guest.GuestID = slotguest.FK_guest 
GROUP BY slotguest.FK_SlotNo;

相关问题