【SQL Server学习笔记】22:集合运算/多集运算(含ALL替代方案)

说是集合运算,实际上可能不是真正意义上的集合,因为查询结果可能包含重复的记录,所以可能不满足集合的元素互异性,但还是使用这样的称呼吧!

基本格式

输入的查询1
<集合运算>
输入的查询2
[ORDER BY子句]

可以看到是对两个查询的结果集,用中间的集合运算来判断每一行是否要包含在运算结果里,并可以用最后的ORDER BY子句对结果集进行排序。

注意,集合(虽然这里是多集,没有互异性)是有无序性的,所以两个被操作的集合(查询)是不能包含ORDER BY子句的,而最后的外部的ORDER BY子句也仅仅是对最终结果集排序输出而已。

此外,参与集合运算的两个查询生成的集合必须包含相同的列数,且相应的列数据类型兼容。

ANSI SQL对以下的每种集合运算都支持DISTINCT和ALL选项。前者对输入的两个多集消除重复行成为真正的集合再参与运算,结果也是集合;后者则不这样做,而仅仅是用两个多集来做运算,结果也是多集。在集合运算里DISTINCT是隐式的,不要写出来。

先建立两张表用来实验:
这里写图片描述
这里写图片描述

并集运算

UNION ALL

直接返回两个多集的所有行为结果集,不会进行比较,也不会删除重复行。

USE MyDB;

SELECT * FROM dbo.ji1
UNION ALL
SELECT * FROM dbo.ji2;

这里写图片描述

UNION(隐含DISTINCT)

这个运算会先把两个多集去重变成集合,然后对集合做并集运算,返回的当然也是集合,不会有重复行。

USE MyDB;

SELECT * FROM dbo.ji1
UNION
SELECT * FROM dbo.ji2;

这里写图片描述

交集运算

INTERSECT(隐含DISTINCT)

这个运算会先把两个多集去重变成集合,然后对集合做交集运算,返回的也是集合。

USE MyDB;

SELECT * FROM dbo.ji1
INTERSECT
SELECT * FROM dbo.ji2;

这里写图片描述

INTERSECT ALL的替代方案

ANSI SQL支持带ALL的INTERSECT运算,意思是对两个多集中产生交集的行,取各自的重复次数中较小的那个(也就是逻辑上只相交了这么多次)。

但SQL Server 2008还没有实现这种运算,下面给出一种替代方案:

用开窗函数对两张多集的表利用要生成集合的列进行分区,得到的就是每个相同元素在一个区里,然后进行排序得到一个区内的行号,并将这个号作为一个新的列。这样,原本区分不开的相同元素就因为这个区内行号的不同而区分开了,这时候,利用普通的INTERSECT就可以得到那些元素相同而且行号相同的了(如123456和1234编号的做普通交集得到的就是1234的,符合前面的要求)。注意ORDER BY子句里SELECT常量表示行的排列顺序不重要。

USE MyDB;

SELECT
    ROW_NUMBER()
        OVER(
            PARTITION BY myint,mychar,mychar2
            ORDER BY (SELECT 0)
            ) AS rownum,
    myint,mychar,mychar2
FROM dbo.ji1
INTERSECT
SELECT
    ROW_NUMBER()
        OVER(
            PARTITION BY uint,uchar,uchar2
            ORDER BY (SELECT 0)
            ) AS rownum,
    uint,uchar,uchar2
FROM dbo.ji2;

这个例子不太好没能凸显出和刚才那个非ALL的结果的区别。
这里写图片描述

然后只要把整个查询上定义一个表表达式(上一节学了),比如CTE,就可以去掉行号了:

USE MyDB;
--下面定义成了一个CTE
WITH INS_ALL
AS
(
SELECT
    ROW_NUMBER()
        OVER(
            PARTITION BY myint,mychar,mychar2
            ORDER BY (SELECT 0)
            ) AS rownum,
    myint,mychar,mychar2
FROM dbo.ji1
INTERSECT
SELECT
    ROW_NUMBER()
        OVER(
            PARTITION BY uint,uchar,uchar2
            ORDER BY (SELECT 0)
            ) AS rownum,
    uint,uchar,uchar2
FROM dbo.ji2
)
--从这个CTE里取出需要的列,就去掉行号了
SELECT myint,mychar,mychar2
FROM INS_ALL;

这里写图片描述

差集运算

EXCEPT(隐含DISTINCT)

同样是先把多集变成集合,然后对集合做差集运算,差集A-B意思就是出现在A中而不出现在B中的那些元素。

USE MyDB;

SELECT * FROM dbo.ji1
EXCEPT
SELECT * FROM dbo.ji2;

这里写图片描述

EXCEPT ALL的替代方案

ANSI SQL支持带ALL的EXCEPT运算,意思是对两个多集中产生交集的行,如果在前一个集合中出现x次,在后一个集合中出现y次,而且x>y,那么在运算结果中这个行将出现x-y次(也就是逻辑上不在后者中发生碰撞的前者中的那些行)。

但SQL Server 2008还没有实现这种运算,下面给出一种替代方案:

和刚才学的INTERSECT ALL的替代方案思想是一样的,只要给重复行开窗成为一个区,然后区内赋予行号,再将这样的两个集合(这回有了区内唯一行号肯定不是多集了)进行普通的EXCEPT运算,如123456和1234号得到的肯定是56号的,就实现了EXCEPT ALL的替代。下面同样还是扔进CTE里然后最后去除行号。

USE MyDB;
--下面定义成了一个CTE
WITH INS_ALL
AS
(
SELECT
    ROW_NUMBER()
        OVER(
            PARTITION BY myint,mychar,mychar2
            ORDER BY (SELECT 0)
            ) AS rownum,
    myint,mychar,mychar2
FROM dbo.ji1
EXCEPT
SELECT
    ROW_NUMBER()
        OVER(
            PARTITION BY uint,uchar,uchar2
            ORDER BY (SELECT 0)
            ) AS rownum,
    uint,uchar,uchar2
FROM dbo.ji2
)
--从这个CTE里取出需要的列,就去掉行号了
SELECT myint,mychar,mychar2
FROM INS_ALL;

这次还是很明显的,多了一行<计算机>,因为在第一个多集中出现了2次,在第二个多集中出现了1次,所以最后要有2-1=1次。
这里写图片描述

集合运算的优先级

INTERSECT的优先级比UNION和EXCEPT的高,UNION和EXCEPT的优先级相等。所以先处理INTERSECT,然后按从左到右的顺序依次处理优先级相同的后两者。

USE MyDB;

SELECT * FROM dbo.ji1
UNION ALL
SELECT * FROM dbo.ji2
INTERSECT
SELECT * FROM dbo.ji1;

这里写图片描述

为楼楼默哀。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值