原文:SQL Server-聚焦INNER JOIN AND IN性能分析(十四)
前言
本节我们来讲讲联接综合知识,我们在大多教程或理论书上都在讲用哪好,哪个性能不如哪个的性能,但是真正讲到问题的实质却不是太多,所以才有了本系列每一篇的篇幅不是太多,但是肯定是我用心去查找许多资料而写出,简短的内容,深入的理解,Always to review the basics。
初次探讨INNER JOIN和IN性能分析
接下来我们看第一篇联接综合知识讲解INNER JOIN和IN的比较分析,我们通过创建表来看INNER JOIN。
创建测试表1
CREATE TABLE Table1 ( id INT IDENTITY PRIMARY KEY, SomeColumn CHAR(4), Filler CHAR(100) )
插入测试数据
Insert into Table1(SomeColumn) Values (1),(2),(3),(4),(5)
创建测试表2并插入数据
USE TSQL2012 GO CREATE TABLE Table2 (IntCol int) Insert into Table2 (IntCol) Values (1),(2),(2),(3),(4),(5),(5)
接下来我们对测试表1和测试表2中的SomeColumn和IntCol进行JOIN
USE TSQL2012 GO SELECT * FROM Table1 b INNER JOIN Table2 s ON b.SomeColumn = s.IntCol
此时我们看到两个测试表中都返回7行数据,因为在测试表2中有重复的数据都匹配上所有测试表1返回所有数据。此时我们再来看看IN的查询
USE TSQL2012 GO SELECT * FROM Table1 WHERE SomeColumn IN (Select IntCol FROM Table2)
此时则返回5条数据,从这里我们知道INNER JOIN和IN还是有很大的区别,但是若在测试表2中没有重复的数据,同时在测试表2中没有需要的列,此时则查询出的数据和测试表1是一样的,此时二者在性能上有什么区别呢?接下来我们在创建大量数据的前提下来进行测试看看。
创建两个测试表
CREATE TABLE BigTable ( id INT IDENTITY PRIMARY KEY, SomeColumn UNIQUEIDENTIFIER NOT NULL, Filler CHAR(100) ) CREATE TABLE SmallerTable ( id INT IDENTITY PRIMARY KEY, LookupColumn UNIQUEIDENTIFIER NOT NULL, SomeArbDate DATETIME DEFAULT GETDATE() )
在BigTable表SomeColumn列中插入100万条数据
INSERT INTO BigTable (SomeColumn) SELECT NEWID() FROM dbo.Nums WHERE n<1000001
取出BigTable中的25%数据插入到SmallerTable表LookupColumn列中
USE TSQL2012 GO INSERT INTO SmallerTable (LookupColumn) SELECT DISTINCT SomeColumn FROM BigTable TABLESAMPLE (25 PERCENT)
这里我们分三种情况来测试。
(1)未建立索引比较INNER和JOIN
SELECT BigTable.ID, SomeColumn FROM BigTable WHERE SomeColumn IN (SELECT LookupColumn FROM dbo.SmallerTable) SELECT BigTable.ID, SomeColumn FROM BigTable INNER JOIN SmallerTable ON dbo.BigTable.SomeColumn = dbo.SmallerTable.LookupColumn
从上看出此时在无论是查询开销还是IO上均没有什么差异,下面我们再来看看建立索引的情况
(2)建立非唯一非聚集索引比较INNER JOIN和IN
CREATE INDEX idx_BigTable_SomeColumn ON BigTable (SomeColumn) CREATE INDEX idx_SmallerTable_LookupColumn ON SmallerTable (LookupColumn)
此时我们发现在建立非唯一非聚集索引的情况二者在查询开销上开始有了比较大的差异,INNER JOIN的开销是IN的两倍而IO几乎是等同的。
(3)建立唯一非聚集索引比较INNER JOIN和IN
CREATE UNIQUE INDEX idx_BigTable_SomeColumn ON BigTable (SomeColumn) CREATE UNIQUE INDEX idx_SmallerTable_LookupColumn ON SmallerTable (LookupColumn)
此时为何索引变为唯一聚集索引二者性能开销却一致了呢?有点纳闷,同时到这里为止是不是说明IN的查询性能比JOIN的性能更好呢,完全颠覆我们的想法,在本文前言我们讨论过在教程中都会给出大部分JOIN比EXISTS性能好,而EXISTS比IN性能好,凡是还是动手实践,亲自验证才是王道,我们只能得出一般性结论:一般来说,JOIN比EXISTS性能好,而EXISTS比IN性能好仅此而已。这都是一般性情况,本系列需要讲述的是什么时候应该用EXISTS,什么时候应该用JOIN,还有什么时候应该用IN,后续内容会陆续讨论这些内容。好了,有点跑题了,上述我们通过100万条数据得出IN的性能接近是INNER JOIN性能的两倍,完全出乎你我的意料,带着这个疑问,接下来我们进一步进行探讨。
进一步探讨INNER JOIN和IN性能分析
上述在SmallerTable表从BigTable表中取出的25%的数据都是唯一的,接下来我们将这25%数据的一部分设置为重复的。我们随便从BigTable表中取出SomeColumn这列的数据,然后将SmallerTable表中的LookupColumn这列的数据设置重复的10000条,如下
USE TSQL2012 GO UPDATE dbo.SmallerTable SET LookupColumn = '0067cb6c-64e1-46cc-b7f2-334a7dd812ff' WHERE id>=1 AND id<=10000
此时我们查询包括重复的这10000条
USE TSQL2012 GO SELECT BigTable.ID, SomeColumn FROM BigTable WHERE SomeColumn IN (SELECT LookupColumn FROM dbo.SmallerTable) SELECT BigTable.ID, SomeColumn FROM BigTable INNER JOIN SmallerTable ON dbo.BigTable.SomeColumn = dbo.SmallerTable.LookupColumn
此时结果还是IN性能比INNER JOIN性能要接近一半,接下来我们在查询SmallerTable表时将重复的LookupColumn列数据去除,此时我们查询变为如下:
USE TSQL2012 GO SELECT BigTable.ID, SomeColumn FROM BigTable WHERE SomeColumn IN (SELECT LookupColumn FROM dbo.SmallerTable) SELECT BigTable.ID, SomeColumn FROM BigTable INNER JOIN (SELECT DISTINCT LookupColumn FROM dbo.SmallerTable) AS s ON s.LookupColumn = dbo.BigTable.SomeColumn
终于查询开销和上述不一样了,此时二者查询性能开销是一致的,相信到了这里我们应该很清楚了。通过上述大量篇幅的贴图和比较我们可以得出INNER JOIN和IN的性能开销使用场景,当我们在初步探讨INNER JOIN和IN的性能分析时,当建立非唯一聚集索引时IN性能接近是INNER JOIN的两倍,而当建立唯一聚集索引时,此时性能开销一致,不免有点纳闷,当我们继续向下探讨时终于明白了这个原因,至此我们最终得出INNER JOIN和IN的性能开销结论。
INNER JOIN和IN性能开销结论:当INNER JOIN表中列数据是唯一的,此时INNER JOIN和IN的性能开销是相同的,当INNER JOIN表中列数据是重复的,此时IN性能要INNER JOIN要好。
总结
本节我们详细叙述了INNER JOIN和IN的性能分析,最终得出一致性结论,下节我们开始讨论NOT EXISTS和NOT IN性能分析,简短的内容,深入的理解,我们下节再会,good night。