我有下表和数据:
CREATE TABLE myTable (
ID INT IDENTITY(1,1) PRIMARY KEY,
Column1 VARCHAR(50),
Column2 VARCHAR(50),
Column3 VARCHAR(50),
Column4 VARCHAR(50),
Column5 VARCHAR(50),
Column6 VARCHAR(50),
Column7 VARCHAR(50),
Column8 VARCHAR(50),
Column9 VARCHAR(50),
Column10 VARCHAR(50)
)
DECLARE @i INT = 1
DECLARE @j INT = 1
DECLARE @distinct_value_count INT = 20
DECLARE @distinct_value_count_with_more_rows INT = 3
DECLARE @rows_per_distinct_value INT = (20000 - (@distinct_value_count_with_more_rows * 2000)) / (@distinct_value_count - @distinct_value_count_with_more_rows)
WHILE @i <= @distinct_value_count
BEGIN
DECLARE @current_rows_per_value INT = @rows_per_distinct_value
IF @i <= @distinct_value_count_with_more_rows
BEGIN
SET @current_rows_per_value = @rows_per_distinct_value + 2000
END
SET @j = 1
WHILE @j <= @current_rows_per_value
BEGIN
INSERT INTO myTable (Column1, Column2, Column3, Column4, Column5, Column6, Column7, Column8, Column9, Column10)
VALUES ('Value' + CAST(@i AS VARCHAR(2)),
'Value' + CAST(@j AS VARCHAR(5)),
'Value' + CAST(@j + 1 AS VARCHAR(5)),
'Value' + CAST(@j + 2 AS VARCHAR(5)),
'Value' + CAST(@j + 3 AS VARCHAR(5)),
'Value' + CAST(@j + 4 AS VARCHAR(5)),
'Value' + CAST(@j + 5 AS VARCHAR(5)),
'Value' + CAST(@j + 6 AS VARCHAR(5)),
'Value' + CAST(@j + 7 AS VARCHAR(5)),
'Value' + CAST(@j + 8 AS VARCHAR(5)))
SET @j = @j + 1
END
SET @i = @i + 1
END
Alter Table dbo.myTable
Add Column11 varchar(50), Column12 varchar(50)
Alter Table dbo.myTable
Add dateModified datetime
Update dbo.myTable
set Column11 = Column1
,Column12 = Column1
Update Top (10) dbo.myTable
Set Column11 = 'Value7'
Where Column1 = 'Value1'
Update Top (10) dbo.myTable
Set Column12 = 'Value7'
Where Column1 = 'Value1'
Update Top (10) dbo.myTable
Set Column11 = 'Value6'
Where Column1 = 'Value1'
Update Top (10) dbo.myTable
Set Column12 = 'Value6'
Where Column1 = 'Value1'
Update Top (10) dbo.myTable
Set Column11 = 'Value5'
Where Column1 = 'Value1'
Update Top (10) dbo.myTable
Set Column12 = 'Value5'
Where Column1 = 'Value1'
Update dbo.myTable
set dateModified = getdate() + ID
CREATE NONCLUSTERED INDEX [Idx_col] ON [dbo].[myTable]
(
[Column1] ASC,
[Column11] ASC,
[Column12] ASC,
[dateModified] ASC
)
INCLUDE([Column5],[Column6])
我必须根据几列进行过滤并返回表中的所有列。为此,我有一个索引涵盖需要过滤的列。我将查询分为两部分:
获取所有满足过滤器的主键行并将它们存储在临时表中。此查询使用非聚集索引。
将此临时表连接回主键列上的主表,以便聚集索引用于获取所有列。
但是,当我尝试这样做时,我遇到了一个问题。在第一个场景中,我将所有过滤的行放入一个临时表中,然后当我将它连接回主表时,它正在执行聚簇索引扫描。在第二种情况下,我只将前 50 行放入临时表,当我将其连接到主表时,它正在执行聚集索引查找。我很困惑为什么会这样。在这两种情况下,临时表上都没有索引。如果有人能帮助我了解发生了什么,我将不胜感激。谢谢你!
场景 1:
SELECT id
INTO #tmpId
FROM myTable
WHERE Column1= 'Value1'
AND Column11 In( 'Value1','Value5','Value6', 'Value7')
And Column12 In ('Value1','Value6')
And dateModified > dateAdd(day,-5, getdate())
SELECT *
FROM myTable m
JOIN #tmpId t
ON m.id = t.id
drop table if exists #tmpId
执行计划场景 1: https://www.brentozar.com/pastetheplan/? id=rkDAD-aLh
场景 2:
SELECT id
INTO #tmpId
FROM myTable
WHERE Column1= 'Value1'
AND Column11 In( 'Value1','Value5','Value6', 'Value7')
And Column12 In ('Value1','Value6')
And dateModified > dateAdd(day,-5, getdate())
Order by dateModified desc offset 0 rows fetch next 50 rows only
SELECT *
FROM myTable m
JOIN #tmpId t
ON m.id = t.id
drop table if exists #tmpId
场景 2 执行计划: https://www.brentozar.com/pastetheplan/? id=rJVbuWaLh
正如 Andy 提到的,您提供的执行计划没有任何问题。SQL 引擎正在按预期工作。根据正在处理的数据的大小,不同的操作会更高效、更快速。Index Seeks 擅长查找少量数据,而 Index Scans 通常更擅长查找大量数据。
将索引想象成电话簿。名称是数据,它按
LastName
then by排序FirstName
。如果您需要查找一个人的电话号码 -John Smith
例如,最快的方法是直接跳转到页面并S...
LastNames
直接跳转到John
该页面上的位置。因此,相当于 Index Seek 操作。现在,对于不同的场景,假设您需要查找每个人的电话号码。您可以任意跳转到'S...'页面然后跳转到
John
,然后跳转到B
页面并跳转到Mary
,然后跳转到G
页面并跳转Tom
到然后跳回到页面S
并跳转到Ralph
。所有这些大量的跳跃都有开销。因为您知道无论如何都需要阅读电话簿中的每个电话号码,所以从第一页的第一个名字开始,然后按顺序阅读电话簿,直到到达最前面的名字会更快(开销更少)最后一页上的姓氏。这相当于索引扫描操作。这基本上就是您的两个查询及其执行计划正在做的事情。
SQL 引擎有一个叫做临界点的东西,它根据正在查找的数据的基数,用作阈值来决定索引扫描何时比索引查找性能更高。但它是一个复杂的算法,不能明确地计算为一个静态值,所以不要担心试图弄清楚它是什么。无论如何,SQL 引擎通常最清楚。
这里没有任何帮助,如前所述,SQL 引擎正在正常工作,为您提供它认为必要的最有效的计划。但如果你的问题更确切地说是它会改变什么,答案是否定的,不太可能。
它使用 Tipping Point 算法所做的选择基于需要查找的行数。该数字不会因为您将聚集索引添加到临时表而改变。
索引搜索有利于检索相对少量的数据。而且它会大大减慢检索大量行的查询。有时,索引查找的不当使用会使查询速度减慢数小时。这就是关于要选择的行数的信息对于查询优化至关重要的原因。
当您选择 50 行时,索引查找工作得很好。但是看起来您 5 天的数据太大,索引查找比索引扫描快。这就是优化器这次使用索引扫描的原因。
您还可以尝试使用 FORCESEEK 和 FORCESCAN 优化器提示来监视差异。