Joh*_*ert 14 sql sql-server performance
除了可能在表中的行数之外,这些示例查询中的一个会比另一个更昂贵吗?
SELECT * FROM dbo.Accounts WHERE AccountID IN (4,6,7,9,10)
SELECT * FROM dbo.Accounts WHERE AccountID NOT IN (4,6,7,9,10)
Run Code Online (Sandbox Code Playgroud)
Mar*_*ith 17
一般来说,NOT IN
虽然肯定有可能构建相反的情景,但是会更昂贵.
首先,假设这AccountId
是Accounts
表的主键.
IN (4,6,7,9,10)
将需要5个索引查找,这意味着逻辑IO是索引深度的5*(每个搜索需要从根向下导航到中间页面并且到达一个叶子页面).
NOT IN (4,6,7,9,10)
将需要一个完整的扫描和一个过滤器(可推送的非sargable谓词意味着它被推入扫描而不是作为一个单独的运算符)这意味着逻辑IO将等于索引的叶节点中的页数+非叶子的数量水平.
看到这个
CREATE TABLE #Accounts
(
AccountID INT IDENTITY(1,1) PRIMARY KEY,
Filler CHAR(1000)
)
INSERT INTO #Accounts(Filler)
SELECT 'A'
FROM master..spt_values
SET STATISTICS IO ON
SELECT * FROM #Accounts WHERE AccountID IN (4,6,7,9,10)
/* Scan count 5, logical reads 10*/
SELECT * FROM #Accounts WHERE AccountID NOT IN (4,6,7,9,10)
/*Scan count 1, logical reads 359*/
SELECT index_depth, page_count
FROM
sys.dm_db_index_physical_stats (2,object_id('tempdb..#Accounts')
, DEFAULT,DEFAULT, 'DETAILED')
Run Code Online (Sandbox Code Playgroud)
返回
index_depth page_count
----------- --------------------
2 358
2 1
Run Code Online (Sandbox Code Playgroud)
看病态不同的情况,其中所有的行都符合IN
条款,因此没有一个符合条款NOT IN
SET STATISTICS IO OFF
CREATE TABLE #Accounts
(
AccountID INT ,
Filler CHAR(1000)
)
CREATE CLUSTERED INDEX ix ON #Accounts(AccountID)
;WITH Top500 AS
(
SELECT TOP 500 * FROM master..spt_values
), Vals(C) AS
(
SELECT 4 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 9 UNION ALL
SELECT 10
)
INSERT INTO #Accounts(AccountID)
SELECT C
FROM Top500, Vals
SET STATISTICS IO ON
SELECT * FROM #Accounts WHERE AccountID IN (4,6,7,9,10)
/*Scan count 5, logical reads 378*/
SELECT * FROM #Accounts WHERE AccountID NOT IN (4,6,7,9,10)
/*Scan count 2, logical reads 295*/
SELECT index_depth,page_count
FROM
sys.dm_db_index_physical_stats (2,OBJECT_ID('tempdb..#Accounts'), DEFAULT,DEFAULT, 'DETAILED')
Run Code Online (Sandbox Code Playgroud)
返回
index_depth page_count
----------- --------------------
3 358
3 2
3 1
Run Code Online (Sandbox Code Playgroud)
(因为uniquifier已添加到聚簇索引键,索引是deper)
在IN
仍实现5平等寻求但这一次阅读每片叶子上的页数追求的是大大超过1.叶子页被安排在一个链表和SQL Server上沿此航行,直到它遇到一个行不匹配进行寻求.
在NOT IN
现在实现为2范围内寻求
[1] Seek Keys[1]: END: #Accounts.AccountID < Scalar Operator((4)),
[2] Seek Keys[1]: START: #Accounts.AccountID > Scalar Operator((4))
Run Code Online (Sandbox Code Playgroud)
用剩余谓词
WHERE ( #Accounts.AccountID < 6
OR #Accounts.AccountID > 6 )
AND ( #Accounts.AccountID < 7
OR #Accounts.AccountID > 7 )
AND ( #Accounts.AccountID < 9
OR #Accounts.AccountID > 9 )
AND ( #Accounts.AccountID < 10
OR #Accounts.AccountID > 10 )
Run Code Online (Sandbox Code Playgroud)
因此可以看出,即使在这种极端情况下,最好的SQL Server也可以跳过仅查看其中一个NOT IN
值的叶页.有点令人惊讶的是,即使我将分布偏斜,使得AccountID=7
记录比AccountID=4
它仍然提供相同计划的记录多6倍,并且没有重写它作为范围寻求7的任何一方,类似地将AccountID=4
记录数量减少 到1计划恢复为聚簇索引扫描,因此似乎仅限于将此转换仅考虑到索引中的第一个值.
在我的答案的前半部分,数字与我的描述和索引深度完全一致.
在第二部分,我的回答没有解释为什么一个具有3个级别和358个叶子页面的索引应该引起它所做的逻辑读取的确切数量,这是因为我不太确定自己的原因!但是我现在已经填补了所缺少的知识.
首先是此查询(仅限SQL Server 2008+语法)
SELECT AccountID, COUNT(DISTINCT P.page_id) AS NumPages
FROM #Accounts
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%) P
GROUP BY AccountID
ORDER BY AccountID
Run Code Online (Sandbox Code Playgroud)
给出了这些结果
AccountID NumPages
----------- -----------
4 72
6 72
7 73
9 72
10 73
Run Code Online (Sandbox Code Playgroud)
加起来NumPages
总共有362个反映了一些叶子页面包含2个不同AccountId
值的事实.搜索将访问这些页面两次.
SELECT COUNT(DISTINCT P.page_id) AS NumPages
FROM #Accounts
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%) P
WHERE AccountID <> 4
Run Code Online (Sandbox Code Playgroud)
给
NumPages
-----------
287
Run Code Online (Sandbox Code Playgroud)
所以,
IN
版本:寻求=4
访问1个根页,1个中间页和72个叶页(74)
寻求=6
访问1个根页,1个中间页和72个叶页(74)
寻求=7
访问1个根页,1个中间页和73个叶页(75)
寻求=9
访问1个根页,1个中间页和72个叶页(74)
寻求=10
访问1个根页,1个中间页和73个叶页(75)
总计:(372)(与IO统计数据中报告的378相比)
NOT IN
版本:寻求<4
访问1个根页,1个中间页和1个叶页 (3)
寻求>4
访问1个根页,1个中间页和287个叶页(289)
总计:(292)(与IO统计数据中报告的295相比)
事实证明,这些与read-ahead
机制有关.(在开发实例上)可以使用跟踪标志来禁用此机制,并验证逻辑读取现在按照上面的描述按预期报告.本博客文章的评论中对此进行了进一步讨论.
NOT IN
基本上意味着全表扫描 - 大部分时间.例外情况是当你有一个索引并且索引的分布很差时(索引中只有很少的值)并且大多数值都处于NOT IN
条件状态.
IN
如果您没有索引,也表示全表扫描.有了索引,它将主要使用索引.另外一个例外是分布不佳的索引或表格中的几行,其中全表扫描更快.
归档时间: |
|
查看次数: |
2338 次 |
最近记录: |