Exists and IN

/*

查询来自'Spain'的客户,对每个匹配的客户,执行在Orders.custid列上的索引执行一次查找操作

检查Orders表是否包含具有该客户的custid的订单

子查询中筛选列custid上的索引在这里非常有帮助,因为通过它可以直接访问Orders表中具有特定custid值的行

*/

SET STATISTICS TIME ON

SELECT custid,companyname

FROM Sales.Customers AS C

WHERE country=N'Spain'

AND custid IN (SELECT custid FROM Sales.Orders)

/*

采用Exists

*/

SELECT *

FROM Sales.Customers AS C

WHERE EXISTS

(

SELECT * FROM Sales.Orders WHERE custid=c.custid

)

AND country='Spain'

/*

这两个查询哪个效率会更高呢

他们逻辑上是相等的2个查询,优化器会为他们生成相同的计划

也许是测试的表数据太少,好吧我们来建2张大表测试一下

*/

--学生表

IF OBJECT_ID('dbo.Sudents') IS NOT NULL

   DROP TABLE dbo.Sudents

CREATE TABLE dbo.Sudents

(

id INT IDENTITY(1,1) PRIMARY KEY,

NAME VARCHAR(16) NULL

)

--学生成绩表

IF OBJECT_ID('dbo.Score') IS NOT NULL

   DROP TABLE dbo.Score

CREATE TABLE dbo.Score

(

id INT IDENTITY(1,1) PRIMARY KEY,

StudentID INT NULL,

Score INT NULL

)

--测试数据

DECLARE @_n INT=1000000

DECLARE @_i INT=1

WHILE @_i<@_n

BEGIN

INSERT INTO dbo.Sudents

SELECT 'Student'+CAST(@_i AS VARCHAR(10))

INSERT INTO dbo.Score

SELECT @@IDENTITY,CAST(CEILING(RAND()*100) AS INT)

SET @_i+=1;

END

--100W条数据确实太多,执行到20+W的时候暂停

--现在删除学号为201的成绩

INSERT INTO dbo.Score

DELETE dbo.Score WHERE StudentID=201

--现在来查询没有成绩的学生

SELECT *

FROM dbo.Sudents AS S

WHERE NOT EXISTS

(

SELECT 1 FROM dbo.Score WHERE StudentID=S.ID

)

/*

SQL Server 执行时间:

   CPU 时间 = 264 毫秒,占用时间 = 105 毫秒。

*/

SELECT *

FROM dbo.Sudents AS S

WHERE id NOT IN

(

SELECT StudentID FROM dbo.Score

)

/*

SQL Server 执行时间:

   CPU 时间 = 297 毫秒,占用时间 = 115 毫秒。

*/

--现在看出Not Exists和Not in的区别了

--给Score表的StudentId加上非聚集索引

CREATE NONCLUSTERED INDEX idx_Score_StudentID ON dbo.Score(StudentID)

--执行时间

/*

  SQL Server 执行时间:

   CPU 时间 = 47 毫秒,占用时间 = 43 毫秒。

SQL Server 执行时间:

   CPU 时间 = 47 毫秒,占用时间 = 46 毫秒。

*/

--加上索引执行时间都缩短了,但两者还是相差不大,那好吧在多删几条记录

DELETE dbo.Score WHERE id IN (202,203,400,600,700,560,951,452)

/*

SQL Server 执行时间:

   CPU 时间 = 31 毫秒,占用时间 = 44 毫秒。

(9 行受影响)

SQL Server 执行时间:

   CPU 时间 = 47 毫秒,占用时间 = 46 毫秒。

*/

--好吧,not exists和not in 在时间效率上还是有区别的

--现在测试一下exists和in 的区别

--清空Score表

TRUNCATE TABLE Score

DECLARE @_m INT=300000

DECLARE @_j INT=210000

WHILE @_j<@_m

BEGIN

INSERT INTO dbo.Score

SELECT @_j,CAST(CEILING(RAND()*100) AS INT)

SELECT @_j+=1

END

--现在查询存在成绩的学生

INSERT INTO Score

SELECT 1000,CAST(CEILING(RAND()*100) AS INT) UNION ALL

SELECT 500,CAST(CEILING(RAND()*100) AS INT) UNION ALL

SELECT 100,CAST(CEILING(RAND()*100) AS INT)

--IN的用法

SET STATISTICS IO OFF

SELECT * FROM Sudents S

WHERE id IN (SELECT StudentID FROM Score )

--Exists的用法

SELECT * FROM Sudents S

WHERE EXISTS

(

SELECT 1 FROM score WHERE StudentId =S.ID

)

/*虽然2者的执行计划是一样的,但时间上效果还是挺明显的

执行时间

SQL Server 执行时间:

   CPU 时间 = 31 毫秒,占用时间 = 21 毫秒。

(3 行受影响)

SQL Server 执行时间:

   CPU 时间 = 16 毫秒,占用时间 = 18 毫秒。

*/

--可能是上面加了索引的原因

--有人会考虑到主表和从表的大小关系,好吧,就当把子查询的表为从表

--现在主表用20+W条记录,从表有9W条记录

--现在在给从表增加到100W条记录

DECLARE @_l INT=1000000

DECLARE @_k INT=300000

WHILE @_k<@_l

BEGIN

INSERT INTO Score

SELECT @_k,CAST(CEILING(RAND()*100) AS INT)

SET @_k+=1

END

--好了 在测试上面的语句

/*

SQL Server 执行时间:

   CPU 时间 = 16 毫秒,占用时间 = 21 毫秒。

(3 行受影响)

SQL Server 执行时间:

   CPU 时间 = 31 毫秒,占用时间 = 19 毫秒。

*/

--还是Exists的效率还是存在一定效率

/*

为什么IN和Exists会有相同的执行计划呢?

如果考虑一下三值逻辑可能就会意识到他们的区别了,与Exists不同,当输入列表中包含NUll时,In实际上会产生一个UNKOWN的逻辑结果,

例如: a IN (b,c,NULL)的结果是UNKOWN,不过在筛选器中UNKOWN和False处理方式类似,使用IN谓词的查询结果与使用Exists谓词一样,

而却优化器知道这一点所以生成了相同的执行计划,另外NOT IN中包含NULL时,NOT IN 查询总会返回空集。因为 val IN(a,b,c,NULL)永远不会返回False

而是返回TRUE、所以val NOT IN (val1,val2.....,NULL)返回的只有NOTtrue 和not Unkown,这两个结果都不会是true

*/

 

posted @ 2012-08-31 15:40  Lordbaby  阅读(207)  评论(0编辑  收藏  举报