整理一下在调查集合性质时经常用到的条件。这些条件可以在HAVING子句中使用,也可以通过SELECT子句在CASE表达式里使用。
各队,全体点名
需要做的是查出现在可以出勤的队伍。可以出勤即队伍里所有队员都处于“待命”状态。使用的是下面这张表。
1 | -- 谓词 |
这个查询性能很好,而且结果中能体现出队员信息,这些是它好的地方。但是因为使用了双重否定,所以理解起来不是很容易。而如果使用HAVING子句,写起来就非常简单了,像下面这样。
1 | -- 集合(1) |
顺便说一下,HAVING子句中的条件还可以像下面这样写。
1 | -- 集合(2) |
极值函数可以使用参数字段的索引,所以这种写法性能更好。
当然也可以把条件放在SELECT子句里,以列表形式显示出各个队伍是否所有队员都在待命,这样的结果更加一目了然。
1 | SELECT team_id, |
需要注意的是,条件移到SELECT子句后,查询可能就不会被数据库优化了,所以性能上相比HAVING子句的写法会差一些。
单重集合与多重集合
例如,有下面这样一张管理各个生产地的材料库存的表。
需要调查出存在重复材料的生产地。
1 | SELECT center |
和前面一样,可以把条件移到SELECT子句中,这样就能在结果中清晰地看到各个生产地是否存在重复材料了。
1 | SELECT center, |
顺便说一下,这个问题也可以通过将HAVING改写成EXISTS的方式来解决。
1 | SELECT center, material |
用EXISTS改写后的SQL语句也能够查出重复的具体是哪一种材料,而且使用EXISTS后性能也很好。
寻找缺失的编号:升级版
下面这样一条查询数列的缺失编号的查询语句。
1 | SELECT '存在缺失的编号' AS gap |
这条SQL语句有一个前提条件,即数列的起始值必须是1.思考一下不管数列的最小值是多少,都能用来判断该数列是否连续的SQL语句。
如果数列的最小值和最大值之间没有缺失的编号,它们之间包含的元素的个数应该是“最大值-最小值+1”。因此,像下面这样写比较条件就可以了。
1 | SELECT '存在缺失的编号' AS gap |
如果不论是否存在缺失的编号,都想要返回结果,那么只需要像下面这样把条件写到SELECT里就可以了。
1 | SELECT CASE WHEN COUNT(*) = 0 THEN '表为空' |
接下来也顺便改进一下查找最小的缺失编号的SQL语句,去掉起始值必须是1的限制。对于像这样表中原本就不存在1的情况,追加一个条件分支让它返回1,即像下面这样来写SQL语句。
1 | SELECT CASE WHEN COUNT(*) = 0 OR MIN(seq) > 1 THEN 1 |
NOT IN改写成了NOT EXISTS,这样写是为了处理值为NULL的情况,以及略微优化一下性能。特别是如果在“seq”列上建立了索引,那么使用NOT EXISTS能明显改善性能。
为集合设置详细的条件
这里以下面这张记录了学生考试成绩的表为例。
- 查询出75%以上的学生分数在80分以上的班级
1 | SELECT class |
- 查询出分数在50分以上的男生的人数比分数在50分以上的女生的人数多的班级
1 | SELECT class |
- 查询出女生平均分比男生平均分高的班级
从表中的数据可以发现,D班全是女生,男生的平均分无法计算。合理的处理方法是,保证对空集求平均的结果是“未定义”。
1 | SELECT class |