SQL计数:不稳定的行为
我编写的一段SQL的行为与预期不符。一个重要的逻辑是计算有多少客人是VIP,但SQL似乎总是得到错误的答案 以下数据库有6位来宾,其中3位是VIPSQL计数:不稳定的行为,sql,count,mariadb,relational-database,Sql,Count,Mariadb,Relational Database,我编写的一段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;
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给出了一个一贯不正确的答案?正如评论中所指出的(从用户@Fábio Amorim、@Rajat处检查),您的查询似乎按预期进行。由于您在时使用了
大小写设置了一个值,因此最好使用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;
闻起来像“爆炸-内爆”。给定
查询是这样执行的:
加入表。假设表的比例不是1:1,这将导致比两个表都多的行
对该临时表进行聚合(例如计数)
只有这样,groupby
才会收缩到最初所需的行数
解决方案是避免使用包含计数/求和数据的多个表进行聚合。有时这种模式是错误的
SELECT ...
FROM ( SELECT x, COUNT(*) AS ct FROM a GROUP BY x ) AS b
JOIN c ON ...
为了解释问题所在,并给出更接近O.p.询问的答案
(我假设O.p.是一个错误的简化示例,而实际查询更为复杂。如果我们知道更大的情况,我想我不会那样编写代码。)
在O.p.查询中,如果guest.vip=1,则1 END
格式不正确。这是一个条件表达式;它应该为查询检索到的所有行返回一个特定值,即guest.vip 1
所在的行
事实上,行为是没有定义的;正如评论所说,它在某些DBMS上产生了预期的答案;根据O.P.的说法,它不会影响其他人。我猜对于那些产生预期答案的人,DBMS将案例
视为返回Null
,然后计数()
忽略Null。这是SQL中Null
的一个更可怕的后果
因此,根据@Fábio Amorim的评论,案例需要一个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;
对我有用。它显示了3
的计数。请参见尝试更改计数(guest.vip=1时为1结束)
了解在Maria DB上测试的总和(guest.vip=1时为1结束)
。对我有用。您的查询不会返回“贵宾人数”。幸运的是,人们能猜出你的意思。使用足够的单词、句子和引用部分例子,清楚、完整地说出你的意思。当给出一个业务关系(ship)/关联或表(base或query result)时,请说明其中的一行根据其列值表示的业务状况。PS在考虑发布之前创建一个。
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;