SQL Server里的INTERSECT
在今天的文章里,我想讨论下SQL Server里的INTERSECT设置操作。INTERSECT设置操作彼此交叉2个记录集,返回2个集里列值一样的记录。下图演示了这个概念。

INTERSECT与INNER JOIN
你会发现,它和2个表间的INNER JOIN几乎一样。但今天我会介绍它们之间的一些重要区别。让我们从创建作为输入的2个简单表开始。
-- Create the 1st table
CREATE TABLE t1
(
Col1 INT,
Col2 INT,
Col3 INT
)
GO -- Create the 2nd table
CREATE TABLE t2
(
Col1 INT,
Col2 INT
)
GO -- Create a unique Clustered Index on both tables
CREATE UNIQUE CLUSTERED INDEX idx_ci ON t1(col1)
CREATE UNIQUE CLUSTERED INDEX idx_ci ON t2(col1)
GO -- Insert some records into both tables
INSERT INTO t1 VALUES (1, 1, 1), (2, 2, 2), (NULL, 3, 3)
INSERT INTO t2 VALUES (2, 2), (NULL, 3)
GO
GO
从T-SQL代码里你可以看到,我也在2个表上创建了唯一聚集索引,并插入了一些测试记录。现在让我们来彼此交叉这2个表:
SELECT Col1, Col2 FROM t1
INTERSECT
SELECT Col1, Col2 FROM t2
GO

SQL Server返回2条记录:列值为2和列值为NULL的记录。这是和INNER JOIN的第1个大区别:如果NULL值出现在2个表里,这些记录会被忽略。当你在Col列上进行2个表之间的INNER JOIN操作,含NULL值的记录不会返回:
SELECT t1.col1, t1.col2 FROM t1
INNER JOIN t2 ON t2.col1 = t1.col1
GO
下图显示了INTERSECT和INNER JOIN方法结果集的不同:

现在我们来分析下INTERSECT设置操作的执行计划。因为在Col列上你有支持的索引,查询优化器可以翻译INTERSECT操作为传统的INNER JOIN逻辑操作。

但这里Nested Loop(Inner Join)并不真正进行INNER JOIN操作。我们来看下为什么。当你查看Nested Loop运算符属性时,你会看到在Clustered Index Seek (Clustered)运算符上有剩余谓语(residual predicate)。

剩余谓语在Col2上评估,因为那列不是刚才创建的聚集索引导航结构的一部分。如我刚开始说的,SQL Server需要在2个表所有列找到匹配的行。使用Clustered Index Seek (Clustered)运算符和剩余谓语,SQL Server只检查在t1表里是否有同样列值的匹配记录。而且Nested Loop运算符本身只返回从一个表的列值——这里是t1表。

因此INNER JOIN只是个左半连接(Left Semi Join):SQL Server检查在右表里是否有我们匹配的记录——如果是的话,匹配的记录从左表返回。Clustered Index Seek (Clustered)上的剩余谓语可以通过提供在导航结构里包含所有必须的列来剔除,如下所示:
-- Create a supporting Non-Clustered Index
CREATE NONCLUSTERED index id_nci ON t1(Col1, Col2)
GO
现在当你再次看INTERSECT运算符的执行计划,你会看到SQL Server在刚才创建的索引进行Index Seek (NonClustered)操作,剩余谓语已经不再需要。

现在当我们删除所有支持的索引结构,我们来看执行计划会变成什么样。
-- Drop all supporting indexes
DROP INDEX id_nci ON t1
DROP INDEX idx_ci ON t1
DROP INDEX idx_ci ON t2
GO
当你再次对2个表进行INTERSECT,现在在执行计划里你会看到Nested Loop (Left Semi Join)运算符。SQL Server现在需要在执行计划里进行左半物理连接,通过在内部上进行Table Scan运算符和在Nested Loop里用剩余谓语进行逐行比较。

这个执行计划并不真的高效,因为在内部Table Scan需要反复进行——对来自外表返回的每一行。如果我们想尽可能高效的进行INTERSECT设置操作,支持的索引非常重要。
小结
INTERSECT设置操作并不可怕,但几乎没人很懂它。当你用它时,你要意识到它和INNER JOIN.之间的区别。你也看到,有很好的索引设计对它非常重要,这样的话查询优化器可以生成很好的执行计划。
感谢关注!
参考文章:
https://www.sqlpassion.at/archive/2015/02/09/intersect-sql-server/
SQL Server里的INTERSECT的更多相关文章
- SQL Server里的INTERSECT ALL
在上一篇文章里,我讨论了INTERSECT设置操作的基础,它和INNER JOIN的区别,还有为什么需要好的索引设计支持.今天我想谈下SQL Server里并未实现的INTERSECT ALL操作. ...
- SQL Server里在文件组间如何移动数据?
平常我不知道被问了几次这样的问题:“SQL Server里在文件组间如何移动数据?“你意识到这个问题:你只有一个主文件组的默认配置,后来围观了“SQL Server里的文件和文件组”后,你知道,有多 ...
- SQL Server里的文件和文件组
在今天的文章里,我想谈下SQL Server里非常重要的话题:SQL Server如何处理文件的文件组.当你用CREATE DATABASE命令创建一个简单的数据库时,SQL Server为你创建2个 ...
- 在SQL Server里我们为什么需要意向锁(Intent Locks)?
在1年前,我写了篇在SQL Server里为什么我们需要更新锁.今天我想继续这个讨论,谈下SQL Server里的意向锁,还有为什么需要它们. SQL Server里的锁层级 当我讨论SQL Serv ...
- SQL Server里的闩锁介绍
在今天的文章里我想谈下SQL Server使用的更高级的,轻量级的同步对象:闩锁(Latch).闩锁是SQL Server存储引擎使用轻量级同步对象,用来保护多线程访问内存内结构.文章的第1部分我会介 ...
- 在SQL Server里为什么我们需要更新锁
今天我想讲解一个特别的问题,在我每次讲解SQL Server里的锁和阻塞(Locking & Blocking)都会碰到的问题:在SQL Server里,为什么我们需要更新锁?在我们讲解具体需 ...
- 在SQL Server里如何进行页级别的恢复
在今天的文章里我想谈下每个DBA应该知道的一个重要话题:在SQL Server里如何进行页级别还原操作.假设在SQL Server里你有一个损坏的页,你要从最近的数据库备份只还原有问题的页,而不是还原 ...
- SQL Server里强制参数化的痛苦
几天前,我写了篇SQL Server里简单参数化的痛苦.今天我想继续这个话题,谈下SQL Server里强制参数化(Forced Parameterization). 强制参数化(Forced Par ...
- SQL Server里ORDER BY的歧义性
在今天的文章里,我想谈下SQL Server里非常有争议和复杂的话题:ORDER BY子句的歧义性. 视图与ORDER BY 我们用一个非常简单的SELECT语句开始. -- A very simpl ...
随机推荐
- Python学习笔记(4):自定义时间类
Python的时间我实在无法接受,太难用了.我觉得C#的时间就非常完美,简单.好用.所以,自定义了自己的时间类: 用法: 一个小小的应用,我需要取出每天股市交易的分钟段,开始是这样的: 稍微改进一下, ...
- js 生成笛卡尔积
其实生成 笛卡尔积的方法原本很简单,for循环就可以了, function discarts() { //笛卡尔积 var twodDscartes = function (a, b) { var r ...
- ASP 中 Cookies 的 Expires 属性的设置(JS版本)
直接上代码,代码中有注释 <%@LANGUAGE="JAVASCRIPT" CODEPAGE="65001"%> <% var numVisi ...
- JavaScript日期对象使用总结
javascript Date日期对象的创建 创建一个日期对象: var objDate=new Date([arguments list]); 我总结了参数形式主要有以下3种: new Date(& ...
- XE6 FMX之控件绘制与显示
中午,有个货随手买的2块钱的彩票,尼玛中了540块,这是啥子狗屎气运.稍微吐槽一下,现在开始正规的笔记录入.经常有朋友说为毛我的博客不更新了或者说更新的少了,为啥呢!一来自己懒了,没学习什么新的东西, ...
- 老罗学习MVC之旅:MVC组件分析
2System.Web.Mvc V 4.0.0.0 组件分析 2.1 Routing组件(路由选择) Routing的作用就是负责分析Url Action的要求• 必须是一个公有方法• 必须返回A ...
- 【WebMisCentral WMC】基于Extjs 4.2x的企业级用户授权认证中心系统(SSO+AM+SM),多租户SAAS应用
http://saas.chinacloudtech.com 题记 三年磨一剑,在企业信息化的道路上已经走了3年之久了,3年多时间里做了很多,突破了很多:有无奈和辛酸,也有收货与喜悦:自我价值也在不断 ...
- 关于BigDecimal的使用
为什么使用BigDecimal 使用BigDecimal首先要注意到float,double是无法支持商业计算的.只能支持工程计算.即误差允许的计算.通常float占用4个字节,32位.double占 ...
- 用Unity模仿CSGO里的火焰效果
CSGO里的火焰效果和真实的情况比较像,能沿着遮挡物前进,如下是模仿效果. 思路比较简单,开始想的是一圈一圈发出去,但是前圈与后圈的联系不好做,换种思路,每个方向发射一条线,这样根据上一个位置的方位先 ...
- windows server 2003 ODBC数据源配置错误
1.ODBC 数据源链接失败,错误 :STATE hy000, 不能产生SSPI上下文, 2.文件服务器,TCP/IP协议属性中,DNS没有填写,填入DNS即可解决.