详解SQL语句的集合运算

以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化。

概述

本篇主要是对集合运算中并集、交集、差集运算基础的总结。

集合运算包含四种:

1.并集运算(两种)

2.交集运算

3.差集运算

下面是集合运算的思维导图:

为什么使用集合运算

1.在集合运算中比联接查询和EXISTS/NOT EXISTS更方便。

在阅读下面的章节时,我们可以先把环境准备好,以下的SQL脚本可以帮助大家创建数据库,创建表,插入数据。

一、集合运算

1.集合运算

(1)对输入的两个集合或多集进行的运算。

(2)多集:由两个输入的查询生成的可能包含重复记录的中间结果集。

(3)T-SQL支持三种集合运算:并集(UNION)、交集(INTERSECT)、差集(EXCEPT)

2.语法

集合运算的基本格式:

输入的查询1

集合运算符>

输入的查询2

[ORDER BY]

3.要求

(1)输入的查询不能包含ORDER BY字句;

(2)可以为整个集合运算结果选择性地增加一个ORDER BY字句;

(3)每个单独的查询可以包含所有逻辑查询处理阶段(处理控制排列顺序的ORDER BY字句);

(4)两个查询 必须包含相同的列数;

(5)相应列必须具有兼容的数据类型。兼容个的数据类型:优先级较低的数据类型必须能隐式地转换为较高级的数据类型。比如输入的查询1的第一列为int类型,输入的查询2的第一列为float类型,则较低的数据类型int类型可以隐式地转换为较高级float类型。如果输入的查询1的第一列为char类型,输入的查询2的第一列为datetime类型,则会提示转换失败:从字符串转换日期和/或时间时,转换失败;

(6)集合运算结果中列名由输入的查询1决定,如果要为结果分配结果列,应该在输入的查询1中分配相应的别名;

(7)集合运算时,对行进行进行比较时,集合运算认为两个NULL相等;

(8)UNION支持DISTINCT和ALL。不能显示指定DISTINCT字句,如果不指定ALL,则默认使用DISTINCT;

(9)INTERSET和EXCEPT默认使用DISTINCT,不支持ALL。

二、UNION(并集)集合运算

1.并集的文氏图

并集:两个集合的并集是一个包含集合A和B中所有元素的集合。

图中阴影区域代表集合A与集合B的并集

2.UNION ALL集合运算

(1)假设Query1返回m行,Query2返回n行,则Query1 UNION ALL Query2返回(m+n)行;

(2)UNION ALL 不会删除重复行,所以它的结果就是多集,而不是真正的集合;

(3)相同的行在结果中可能出现多次。

3.UNION DISTINCT集合运算

(1)假设Query1返回m行,Query2返回n行,Query1和Query2有相同的h行,则Query1 UNION Query2返回(m+n-h)行;

(2)UNION 会删除重复行,所以它的结果就是集合;

(3)相同的行在结果中只出现一次。

(4)不能显示指定DISTINCT字句,如果不指定ALL,则默认使用DISTINCT。

(5)当Query1与Query2比较某行记录是否相等时,会认为取值为NULL的列是相等的列。

三、INTERSECT(交集)集合运算

1.交集的文氏图

交集:两个集合(记为集合A和集合B)的交集是由既属于A,也属于B的所有元素组成的集合。

图中阴影区域代表集合A与集合B的交集

2.INTERSECT DISTINCT集合运算

(1)假设Query1返回 m 行,Query2返回 n 行,Query1和Query2有相同的 h 行,则Query1 INTERSECT Query2返回 h 行;

(2)INTERSECT集合运算在逻辑上首先删除两个输入多集中的重复行(把多集变为集合),然后返回只在两个集合中都出现的行;

(3)INTERSECT 会删除重复行,所以它的结果就是集合;

(4)相同的行在结果中只出现一次。

(5)不能显示指定DISTINCT字句,如果不指定ALL,则默认使用DISTINCT。

(6)当Query1与Query2比较某行记录是否相等时,会认为取值为NULL的列是相等的列。

(7)用内联接或EXISTS谓词可以代替INTERSECT集合运算,但是必须对NULL进行处理,否则这两种方法对NULL值进行比较时,比较结果都是UNKNOWN,这样的行会被过滤掉。

3.INTERSECT ALL集合运算

(1)ANSI SQL支持带有ALL选项的INTERSECT集合运算,但SQL Server2008现在还没有实现这种运算。后面会提供一种用于T-SQL实现的替代方案;

(2)假设Query1返回 m 行,Query2返回 n 行,如果行R在Query1中出现了x次,在Query2中出现了y次,则行R应该在INTERSECT ALL运算之后出现minimum(x,y)次。

下面提供用于T-SQL实现的INTERSECT ALL集合运算:公用表表达式 + 排名函数

结果如下:

其中UK NULL London有四个重复行,

在排序函数的OVER字句中使用 ORDER BY ( SELECT 常量> )可以告诉SQL Server不必在意行的顺序。

四、EXCEPT(差集)集合运算

1.差集的文氏图

差集:两个集合(记为集合A和集合B)的由属于集合A,但不属于集合B的所有元素组成的集合。

图中阴影区域代表集合A与集合B的差集

2.EXCEPT DISTINCT集合运算

(1)假设Query1返回 m 行,Query2返回 n 行,Query1和Query2有相同的 h 行,则Query1 INTERSECT Query2返回 m – h 行,而Query2 INTERSECT Query1 返回 n – h 行

(2)EXCEPT集合运算在逻辑上先删除两个输入多集中的重复行(把多集转变成集合),然后返回只在第一个集合中出现,在第二个集合众不出现所有行。

(3)EXCEPT 会删除重复行,所以它的结果就是集合;

(4)EXCEPT是不对称的,差集的结果取决于两个查询的前后关系。

(5)相同的行在结果中只出现一次。

(6)不能显示指定DISTINCT字句,如果不指定ALL,则默认使用DISTINCT。

(7)当Query1与Query2比较某行记录是否相等时,会认为取值为NULL的列是相等的列。

(8)用左外联接或NOT EXISTS谓词可以代替INTERSECT集合运算,但是必须对NULL进行处理,否则这两种方法对NULL值进行比较时,比较结果都是UNKNOWN,这样的行会被过滤掉。

3.EXCEPT ALL集合运算

(1)ANSI SQL支持带有ALL选项的EXCEPT集合运算,但SQL Server2008现在还没有实现这种运算。后面会提供一种用于T-SQL实现的替代方案;

(2)假设Query1返回 m 行,Query2返回 n 行,如果行R在Query1中出现了x次,在Query2中出现了y次,且x>y,则行R应该在EXCEPT ALL运算之后出现 x – y 次。

下面提供用于T-SQL实现的EXCEPT ALL集合运算:公用表表达式 + 排名函数

  1. WITH    INTERSECT_ALL
  2. AS (
  3.  SELECT   ROW_NUMBER() OVER ( PARTITION BY country, region, city ORDER BY ( SELECT
  4. 0
  5. ) ) AS rownum ,
  6. country ,
  7. region ,
  8. city
  9. FROM     HR.Employees
  10. EXCEPT
  11. SELECT   ROW_NUMBER() OVER ( PARTITION BY country, region, city ORDER BY ( SELECT
  12. 0
  13. ) ) AS rownum ,
  14. country ,
  15. region ,
  16. city
  17. FROM     Sales.Customers
  18. )
  19. SELECT  country ,
  20. region ,
  21. city
  22. FROM    INTERSECT_ALL

结果如下:

五、集合运算的优先级

1.INTERSECT>UNION=EXCEPT

2.首先计算INTERSECT,然后从左到右的出现顺序依次处理优先级的相同的运算。

3.可以使用圆括号控制集合运算的优先级,它具有最高的优先级。

六、特殊处理

1.只有ORDER BY能够直接应用于集合运算的结果;

2.其他阶段如表运算符、WHERE、GROUP BY、HAVING等,不支持直接应用于集合运算的结果,这个时候可以使用表表达式来避开这一限制。如根据包含集合运算的查询定义个表表达式,然后在外部查询中对表表达式应用任何需要的逻辑查询处理;

3.ORDER BY字句不能直接应用于集合运算中的单个查询,这个时候可以TOP+ORDER BY字句+表表达式来避开这一限制。如定义一个基于该TOP查询的表表达式,然后通过一个使用这个表表达式的外部查询参与集合运算。

七、练习题

1.写一个查询,返回在2008年1月有订单活动,而在2008年2月没有订单活动的客户和雇员。

期望结果:

方案一:EXCEPT

(1)先用查询1查询出2008年1月份有订单活动的客户和雇员

(2)用查询2查询2008年2月份客户的订单活动的客户和雇员

(3)用差集运算符查询2008年1月有订单活动而2008年2月没有订单活动的客户和雇员

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate

方案二:NOT EXISTS

必须保证custid,empid不能为null,才能用NOT EXISTS进行查询,如果custid或empid其中有null值存在,则不能用NOT EXISTS进行查询,因为比较NULL值的结果是UNKNOWN,这样的行用NOT EXISTS查询返回的子查询的行会被过滤掉,所以最后的外查询会多出NULL值的行,最后查询结果中会多出NULL值的行。

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders AS O1
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate

如果我往Sales.Orders表中插入两行数据:

插入cutid=NULL,empid=1,orderdate=‘20080101’

  1. INSERT  INTO [TSQLFundamentals2008].[Sales].[Orders]
  2. ( [custid] ,
  3. [empid] ,
  4. [orderdate] ,
  5. [requireddate] ,
  6. [shippeddate] ,
  7. [shipperid] ,
  8. [freight] ,
  9. [shipname] ,
  10. [shipaddress] ,
  11. [shipcity] ,
  12. [shipregion] ,
  13. [shippostalcode] ,
  14. [shipcountry]
  15. )
  16. VALUES  ( NULL ,
  17. 1 ,
  18. ‘20080101‘ ,
  19. ‘20080101‘ ,
  20. ‘20080101‘ ,
  21. 1 ,
  22. 1 ,
  23. ‘A‘ ,
  24. ‘20080101‘ ,
  25. ‘A‘ ,
  26. ‘A‘ ,
  27. ‘A‘ ,
  28. ‘A‘
  29. )
  30. GO

插入cutid=NULL,empid=1,orderdate=‘20080201’

  1. INSERT  INTO [TSQLFundamentals2008].[Sales].[Orders]
  2. ( [custid] ,
  3. [empid] ,
  4. [orderdate] ,
  5. [requireddate] ,
  6. [shippeddate] ,
  7. [shipperid] ,
  8. [freight] ,
  9. [shipname] ,
  10. [shipaddress] ,
  11. [shipcity] ,
  12. [shipregion] ,
  13. [shippostalcode] ,
  14. [shipcountry]
  15. )
  16. VALUES  ( NULL ,
  17. 1 ,
  18. ‘20080201‘ ,
  19. ‘20080101‘ ,
  20. ‘20080101‘ ,
  21. 1 ,
  22. 1 ,
  23. ‘A‘ ,
  24. ‘20080101‘ ,
  25. ‘A‘ ,
  26. ‘A‘ ,
  27. ‘A‘ ,
  28. ‘A‘
  29. )
  30. GO

用方案一查询出来结果为50行,会把cutid=NULL,empid=1的行过滤掉

用方案二查询出来结果为51行,不会把cutid=NULL,empid=1的行过滤掉

用下面的方案可以解决上面的问题,需要处理cutid=NULL,或者empid=null的情况。返回50行

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders AS O1
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate

2.写一个查询,返回在2008年1月和在2008年2月都有订单活动的客户和雇员。

期望结果:

方案一:INTERSECT

(1)先用查询1查询出2008年1月份有订单活动的客户和雇员

(2)用查询2查询2008年2月份客户的订单活动的客户和雇员

(3)用交集运算符查询2008年1月和2008年2月都有订单活动的客户和雇员

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate

方案二:EXISTS

必须保证custid,empid不能为null,才能用EXISTS进行查询,如果custid或empid其中有null值存在,则不能用EXISTS进行查询,因为比较NULL值的结果是UNKNOWN,这样的行用EXISTS查询返回的子查询的行会被过滤掉,所以最后的外查询会少NULL值的行,最后查询结果中会少NULL值的行。

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders AS O1
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate

如果我往Sales.Orders表中插入两行数据:

插入cutid=NULL,empid=1,orderdate=’20080101′

插入cutid=NULL,empid=1,orderdate=’20080201′

用方案一查询出来结果为6行,不会把cutid=NULL,empid=1的行过滤掉

用方案二查询出来结果为5行,会把cutid=NULL,empid=1的行过滤掉

用下面的方案可以解决上面的问题,需要处理cutid=NULL,或者empid=null的情况。返回6行。

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders AS O1
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate

3.写一个查询,返回在2008年1月和在2008年2月都有订单活动,而在2007年没有订单活动的客户和雇员

期望结果:

方案一:INTERSECT + EXCEPT

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate = ‘20070101‘
  7. AND orderdate

方案二:EXISTS + NOT EXISTS

  1. SELECT  custid ,
  2. empid
  3. FROM    Sales.Orders AS O1
  4. WHERE   orderdate >= ‘20080101‘
  5. AND orderdate = ‘20080201‘
  6. AND orderdate = ‘20070101‘
  7. AND orderdate

原文地址:https://www.cnblogs.com/syncnavigator/p/10198381.html

时间: 2024-08-08 12:54:11

详解SQL语句的集合运算的相关文章

[Android新手区] SQLite 操作详解--SQL语法

该文章完全摘自转自:北大青鸟[Android新手区] SQLite 操作详解--SQL语法  :http://home.bdqn.cn/thread-49363-1-1.html SQLite库可以解析大部分标准SQL语言.但它也省去了一些特性并且加入了一些自己的新特性.这篇文档就是试图描述那些SQLite支持/不支持的SQL语法的.查看关键字列表. 如下语法表格中,纯文本用蓝色粗体显示.非终极符号为斜体红色.作为语法一部分的运算符用黑色Roman字体表示. 这篇文档只是对SQLite实现的SQ

1.SQL优化系列-->高手详解SQL性能优化十条经验

1.查询的模糊匹配 尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用. 解决办法: 其实只需要对该脚本略做改进,查询速度便会提高近百倍.改进方法如下: a.修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了. b.直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个

详解SQL集合运算

以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [

【PLSQL】详解SQL中的trigger(触发器)

本篇主要内容如下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触发器 8.1.3 系统触发器 8.2 创建触发器 8.2.1 触发器触发次序 8.2.2 创建DML触发器 8.2.3 创建替代(INSTEAD OF)触发器 8.2.3 创建系统事件触发器 8.2.4 系统触发器事件属性 8.2.5 使用触发器谓词 8.2.6 重新编译触发器 8.3 删除和使能触发器 8.4 触发器和数据字典 8.5   数据库触发器的应用举例 触发器是许多关系数据库系统都提供的一项技术.在O

SQL语句实例集合

SQL语句实例 表操作     例 1  对于表的教学管理数据库中的表 STUDENTS ,可以定义如下: CREATE  TABLE  STUDENTS (SNO      NUMERIC (6, 0) NOT NULL SNAME    CHAR (8) NOT NULL AGE      NUMERIC(3,0) SEX      CHAR(2) BPLACE  CHAR(20) PRIMARY KEY(SNO)) 例 2  对于表的教学管理数据库中的表 ENROLLS ,可以定义如下:

深入详解SQL中的Null

NULL 在计算机和编程世界中表示的是未知,不确定.虽然中文翻译为 "空", 但此空(null)非彼空(empty). Null表示的是一种未知状态,未来状态,比如小明兜里有多少钱我不清楚,但也不能肯定为0,这时在计算机中就使用Null来表示未知和不确定. 虽然熟练掌握SQL的人对于Null不会有什么疑问,但总结得很全的文章还是很难找,看到一篇英文版的, 感觉还不错. Tony Hoare 在1965年发明了 null 引用, 并认为这是他犯下的"几十亿美元的错误"

SQL Server数据库培训(SQL篇)----集合运算及常用函数

1.             集合运算及常用函数 1.1          字符转换函数 1.1.1             ASCII () 返回字符表达式最左端字符的ASCII 码值.在ASCII()函数中,纯数字的字符串可不用''括起来,但含其它字符的字符串必须用''括起来使用,否则会出错. SELECT ASCII('iTalkbb') ---------- 105 1.1.2             CHAR () 将ASCII 码转换为字符.如果没有输入0 ~ 255 之间的ASCI

高手详解SQL性能优化十条经验

1.查询的模糊匹配 尽量避免在一个复杂查询里面使用 LIKE '%parm1%'—— 红色标识位置的百分号会导致相关列的索引无法使用,最好不要用. 解决办法: 其实只需要对该脚本略做改进,查询速度便会提高近百倍.改进方法如下: a.修改前台程序——把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了. b.直接修改后台——根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个

详解SQL盲注测试高级技巧

写在前面: 这篇文章主要写了一些加快盲注速度的技巧和盲注中比较精巧的语句,虽然注入并不是什么新技术了.但是数据库注入漏洞依然困扰着每一个安全厂商,也鞭策着每一个安全从业者不断前进. 正文: 首先来简单介绍一下盲注,盲注是不能通过直接显示的途径来获取数据库数据的方法.在盲注中,攻击者根据其返回页面的不同来判断信息(可能是页面内容的不同,也可以是响应时间不同).一般情况下,盲注可分为三类. Booleanbase Timebase Errorbase 其中第一类Boolean就是我们最常接触到的普通