SQL进阶教程——用SQL进行集合运算(第七章)

mac2024-11-14  9

1. 前言

集合论是SQL语言的根基——这是贯穿全书主题之一。因为他的这个特性,SQL也被称为面向集合语言。只有从集合的角度思考,才能明白SQL的强大。 但是,实际上这一点长期以来都被很多人忽略了。造成这种状况,SQL本身也是要负一定责任的。其实,在很长一段时间内,SQL连我们在高中学习过的基础的集合运算符都没有。UNION是SQL-86标准开始加入的,还算比较早,而INTERSECT和EXCEPT都是SQL-92标准才加入的。至于关系除法,更是至今还没有被标准化。

2. 集合运算的几个注意事项

SQL能操作具有重复行的集合,可以通过可选项ALL来支持 一般的集合论是不允许集合里存在重复元素的,因此集合{1,1,2,3,3,3}和集合{1,2,3}被视为相同的集合。但是关系数据库里的表允许存在重复的行,称为多重集合。 因此,SQL的集合运算符也提供了允许重复和不允许重复的两种用法。如果直接使用UNION和INTERSECT,结果里就不会出现重复行。如果想在结果里留下重复行,可以加上可选项ALL,写作UNION ALL。 出了运算结果之外,这两种用法还有一个不同。集合运算符为了排除重复行,默认的会发生排序,而加上可选项ALL之后就不会排序,所以性能会有提升。如果不关心结果是否存在重复行,或者确认结果不会产生重复行,可以加上ALL集合运算符有优先级 标准SQL规定,INTERSECT比UNION和EXCEPT优先级更高。因此,当同时使用UNION和INTERSECT,又想让UNION优先执行时,必须用括号明确的指定运算顺序。各个DBMS提供商在集合运算的实现程度上参差不齐除法运算没有标准定义

3. 比较表和表:检查集合相等性之基础表

这里说的“相等”指的是行数和列数以及内容都相同,即是“同一个集合”的意思

select count(1) row_cnt from ( select * from tbl_A union select * from tbl_b ) tmp ;

4. 比较表和表:检查集合相等性之进阶篇

SELECT CASE WHEN COUNT(*) = 0 THEN '相等' ELSE ‘不相等’ END AS result FROM ((SELECT * FROM TB1_A UNION SELECT * FROM TB1_B) EXCEPT (SELECT * FROM TB1_A INTERSECT SELECT * FROM TB1_B) )) TMP; /**这个改进版本连事先查询两张表的行数这种准备工作也不需要了,但是功能改进了, 却也带来了一些缺陷。由于这里需要四次排序,所以性能会有所下降**/

5. 用差集实现关系除法运算

在本节开头说过,SQL里还没有能直接进行关系除法运算的运算符。因此,为了进行除法运算,必须自己实现。方法比较多,其中具有代表性的是下面三个

嵌套使用NOT EXISTS使用HAVING子句转化成一对一关系把除法变成减法 Skills skill(技术)OracleUNIXJava EmpSkills emp(员工)skill(技术)相田Oracle相田UNIX相田Java相田C#神崎Oracle神崎UNIX神崎Java平井UNIX平井Oracle平井PHP平井Perl平井C++若田部Perl渡来Oracle 从表EmpSkills中找出精通表Skills中所有技术的员工 --用求差的方法进行关系除法运算 select distinct emp from empskills es1 where not exists (select skill from skills expect select skill from empskills es2 where es1.emp = es2.emp); /**理解这段代码的要点在于EXCEPT运算符和关联子查询。 关联子查询建立在表Empskills上,这是因为,我们要针对每个员工进行集合运算。 即从集合的技术的集合中减去每个员工自己的技术的集合,结果集合为空集 则说明该员工具备所有的需求的技术,否则说明该员工不具备某些需求的技术**/

注意:

关联子查询是为了使SQL能够实现类似面向过程语言中循环的功能而引入的

6. 寻找相等的子集

SupParts A螺丝A螺母A管子B螺丝B管子C螺丝C螺母C管子D螺丝D管子E保险丝E螺母E管子F保险丝

这个问题看起来简单,但是SQL并没有提供任何用于检查集合的包含关系或相等性的谓词,并且比较的双方都不固定,这次我们需要比较所有子集的全部组合,所以这个问题更具有普遍性

--找出经营的零件在种类数和种类上都完全相同的供应商组合 select a.sup s1, b.sup s2 from supparts a, supparts b where a.sup < b.sup -- 生成供应商的全部组合 and a.part = b.part -- 条件1:经营同种类型的零件 group by a.sup, b.sup having count(*) = (select count(1) -- 条件2:经营的零件的数量种类相同 a = 中间数 from supparts c where c.sup = a.sup) and count(*) = (select count(1) -- 条件2:经营的零件的数量种类相同 b = 中间数 from supparts d where d.sup = b.sup) /**如果我们把HAVING子句里的两个条件当初精准关系除法运算,机会很好理解 加上这两个条件,我们就能保证集合A和集合B的元素个数一致,不会出现不足或者过剩(即存在一一映射) 而且条件一又保证了经营的零件类型也都是完全相同的**/ ;

7. 用于删除重复行的高效SQL

Products name(商品名称)price(价格)苹果50橘子100橘子100橘子100香蕉80 前面介绍的是使用关联子查询 DELETE FROM Product WHERE rowid < (SELECT MAX(P2.rowid) FROM Products P2 WHERE Products.name = P2.name AND Products.price=P2.name); /**关联子查询的性能较差(光是DELETE处理就比较耗时了) **/ -- 删除重行高效SQL语句(1):通过EXCEPT求补集 delete from products where rowid in (select rowid -- 全部rowid from products except -- 减去 select max(rowid) -- 要留下的rowid from products group by name1, price ); -- 删除重行高效SQL语句(2):通过NOT IN求补集 delete from products where rowid not in (select max(rowid) from products group by name1, price );

本节要点:

在集合运算方面,SQL的标准化进行的比较缓慢,直到现在也是实现状况因数据库不同而参差不齐,因此使用的时候需要注意如果集合运算符不指定ALL可选项,重复行会被排除掉,而且这种情况还会发生排序,所以性能方面不够好UNION和INTERSECT都具有幂等性这一重要性质,而EXCEPT不具有幂等性标准SQL没有关系除法的运算符,需要自己实现判断两个集合是否相等时,可以通过幂等性或一一映射两种方法使用EXCEPT可以很简单的求得补集
最新回复(0)