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 ...
随机推荐
- 如何实现在H5里调起高德地图APP?(上)
这一篇文章,将讲述如何在H5里调起高德地图APP,并展示兴趣点.适合于展示某个餐馆,商场等,让用户自行选择前往方式. 场景一.在高德地图上展示Marker点或者POI标记 在一些基于位置分享的应用开发 ...
- JS中的各种检测
//null 只在肯定返回null值时才使用null比较 var element = document.getElementById("my-div"); if (element ...
- Bilateral Filtering(双边滤波) for SSAO(转)
原文链接:http://blog.csdn.net/bugrunner/article/details/7170471 另外一篇相似的英文资料:http://homepages.inf.ed.ac.u ...
- BizTalk 2013R2 WCF-LOB Oracle Adapter安装配置/问题&解决方法
BizTalk 2013R2 WCF-LOB Oracle Adapter安装配置/问题&解决方法 安装Oracle Adapter 安装Oracle客户端 BizTalk 2013R2 安装 ...
- Objective C for Windows
You can use Objective C inside the Windows environment. If you follow these steps, it should be work ...
- blueImp/jQuery file upload 的正确用法(限制上传大小和文件类型)
这个插件太出名了,几乎能完成所有能想象的到的效果,包括进度条.拖拽.甚至现在已经完美支持图片视频等的处理,三个字形容就是屌爆了.最近在做上传这一部分,发现网上对于上传文件大小的限制和类型检测等的方法都 ...
- THEOS makefile
转自https://www.h4ck.org.cn/2013/07/theos-makefile/ theos的makefile写法与其他linux/unix环境下的makefile写法大同小异,但是 ...
- GitHub 优秀的 Android 开源项目(转)
今天查找资源时看到的一篇文章,总结了很多实用资源,十分感谢原作者分享. 转自:http://blog.csdn.net/shulianghan/article/details/18046021 主要介 ...
- 错误名称:EntityCommandExecutionException
错误名称:EntityCommandExecutionException 错误时间:2015/9/22 11:13:34 错误消息:执行命令定义时出错.有关详细信息,请参阅内部异常. 堆栈信息: 在 ...
- 怎么删除github上的仓库
1.到你的个人中心.点击你的个人账号.下图的红色部分 2.点击repositories(仓库),选择你要删除的项目 3.code这一行导航栏 最后的一个. setting 4.下拉页面到最下面 Del ...