集合运算的几个注意事项
顾名思义,集合运算符的参数是集合,从数据库实现层面上来说就是表或者视图。
- SQL能操作具有重复行的集合,可以通过可选项ALL来支持
- 集合运算符为了排除掉重复行,默认地会发生排序,而加上可选项ALL之后,就不会再排序,所以性能会有提升。
- 集合运算符有优先级
- INTERSECT比UNION和EXCEPT优先级更高。
- 各个DBMS提供商在集合运算的实现程度上参差不齐
- 除法运算没有标准定义
比较表和表:检查集合相等性之基础篇
在迁移数据库的时候,或者需要比较备份数据和最新数据的时候,需要调查两张表是否是相等的,即“是同一个集合”的意思。例如,下面的表tbl_A和tbl_B。
这两张表的行数是3。如果下面这条SQL语句的执行结果是3,则说明两张表是相等的。相反,如果结果大于3,则说明两张表不相等。
1 | SELECT COUNT(*) AS row_cnt |
前面的SQL语句可以用于包含NULL数据的表,而且不需要指定列数、列名和数据类型等就能使用。
比较表和表:检查集合相等性之进阶篇
在前面的解法中,在比较两张表之前,需要先查一下两张表里数据的行数。改进一下这条SQL语句,利用两个集合的并集和差集来判定其相等性。如果用SQL语言描述,那就是“如果A UNION B = A INTERSECT B,则集合A和集合B相等。”
因此,只需要判定(A UNION B) EXCEPT (A INTERSECT B)的结果集是不是空集就可以了。如果A=B,则这个结果集是空集,否则,这个结果集里肯定有数据。
1 | SELECT CASE WHEN COUNT(*) = 0 THEN '相等' ELSE '不相等' END AS result |
这条SQL语句与上一部分的SQL语句具有相同的优点,也不需要知道列名和列数,还可以用于包含NULL的表,而且,这个改进版连事先查询两张表的行数这种准备工作也不需要了。但是,虽然功能改进了,却也带来了一些缺陷。由于这里需要进行4次排序(3次集合运算加上1次DISTINCT),所以性能会有所下降。
用差集实现关系除法运算
关于示例数据,选用的是下面这两张员工技术信息管理表。
这里的问题是,从表EmpSkills中找出精通表Skills中所有技术的员工。
1 | SELECT DISTINCT emp |
从需求的技术的集合中减去每个员工自己的技术的集合,如果结果是空集,则说明该员工具备所有的需求的技术,否则说明该员工不具备某些需求的技术。
寻找相等的子集
使用表示“供应商-零件”关系的表作为示例数据。
需要求的是,经营的零件在种类数和种类上都完全相同的供应商组合。
1 | SELECT |
用于删除重复行的高效SQL
看一下没有主键的恐怖的表。
使用关联子查询,代码非常简单。
1 | DELETE FROM Products |
这种做法不算太差,只是关联子查询的性能问题是难点(光是DELETE处理就比较耗时了)。因此,这里思考一下如何不用关联子查询也能实现同样的功能。
“使用极值函数让每组只留下一个rowid“这一点与之前的做法一样。不同的是,这次需要把留下的集合从表Products这个集合中减掉。SQL语句如下所示。
1 | -- 通过EXCEPT求补集 |
此外,把EXCEPT改写成NOT IN也是可以实现的。代码如下所示。
1 | -- 通过NOT IN求补集 |
这两种方法的性能优劣主要取决于表的规模,以及删除的行数与留下的行数之间的比率。