【发布时间】:2020-10-14 10:42:11
【问题描述】:
我们有一个应用程序执行一些我们无法更改的查询,如下所示(我使用 StackOverflow2013 数据库来演示它):
SELECT *
FROM dbo.Posts p
WHERE CHARINDEX(N'Aptana', p.Title) > 0
我们的数据库具有类似的结构 - 行很宽,由许多不同的列组成,包括 nvarchar(smth) 和 nvarchar(max) 数据类型。
这个查询有这个查询计划(就像我们的,聚集索引扫描),很明显:
|--Clustered Index Scan(OBJECT:([StackOverflow2013].[dbo].[Posts].[PK_Posts_Id] AS [p]), WHERE:(charindex(N'Aptana',[StackOverflow2013].[dbo].[Posts].[Title] as [p].[Title])>(0)))
actual execution plan with clustered index scan
我们在这个专栏上有一个索引,我在 dbo.Posts (Title) 上创建了一个:
CREATE INDEX myPleasureSort ON dbo.Posts (Title);
我无法更改查询,但我可以创建索引并使用计划指南添加 INDEX HINT。 我不得不说,我们的用户总是使用这种查询来查找几行,可能是 5000 万行中的 100 行,因此非聚集索引扫描应该更快且资源消耗更少。
所以当我尝试这个时:
SELECT *
FROM dbo.Posts p
WHERE CHARINDEX(N'Aptana', p.Title) > 0
OPTION (MAXDOP 1, TABLE HINT(p, INDEX (myPleasureSort)))
结果如下:
|--Nested Loops(Inner Join, OUTER REFERENCES:([p].[Id], [Expr1002]) WITH UNORDERED PREFETCH)
|--Sort(ORDER BY:([p].[Id] ASC))
| |--Index Scan(OBJECT:([StackOverflow2013].[dbo].[Posts].[myPleasureSort] AS [p]), WHERE:(charindex(N'Aptana',[StackOverflow2013].[dbo].[Posts].[Title] as [p].[Title])>(0)))
|--Clustered Index Seek(OBJECT:([StackOverflow2013].[dbo].[Posts].[PK_Posts_Id] AS [p]), SEEK:([p].[Id]=[StackOverflow2013].[dbo].[Posts].[Id] as [p].[Id]) LOOKUP ORDERED FORWARD)
actual execution plan with key lookup and sort
这是我的问题。为什么我在 Key Lookup 之前有这个排序操作?我想正因为如此,我获得了巨额的内存补助,但我不希望它在生产中使用。
查询内存授权检测到“ExcessiveGrant”,这可能会影响 可靠性。授权大小:初始 566496 KB,最终 566496 KB,使用 216 知识库。
我找到了这个索引的解决方法:
CREATE INDEX myPleasure ON dbo.Posts (Id, Title);
对于这个查询,我有下一个查询计划:
SELECT *
FROM dbo.Posts p
WHERE CHARINDEX(N'Aptana', p.Title) > 0
OPTION (MAXDOP 1, TABLE HINT(p, INDEX (myPleasure)))
|--Nested Loops(Inner Join, OUTER REFERENCES:([p].[Id], [Expr1002]) WITH UNORDERED PREFETCH)
|--Index Scan(OBJECT:([StackOverflow2013].[dbo].[Posts].[myPleasure] AS [p]), WHERE:(charindex(N'Aptana',[StackOverflow2013].[dbo].[Posts].[Title] as [p].[Title])>(0)) ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([StackOverflow2013].[dbo].[Posts].[PK_Posts_Id] AS [p]), SEEK:([p].[Id]=[StackOverflow2013].[dbo].[Posts].[Id] as [p].[Id]) LOOKUP ORDERED FORWARD)
actual execution plan with key lookup without sort
但我更喜欢仅在 nvarchar 列上使用索引,以便有可能将其与 LIKE 'str%' 之类的内容一起使用。
提前谢谢你,请执行我糟糕的英语。
更新:选择@@VERSION:
Microsoft SQL Server 2017 (RTM-CU20) (KB4541283) - 14.0.3294.2 (X64)
更新 2:感谢@MattM,它看起来像我的情况:Why is Sort operation before Nested Loops (Inner Join)?
【问题讨论】:
-
WHERE CHARINDEX(N'Aptana', p.Title) > 0不是 SARGable,因此索引无济于事。因此,SQL Server 感觉最简单的方法是扫描整个聚集索引(这可能是最快的,因为它将成为覆盖索引)/ -
谢谢,我明白了。但我希望 SQL Server 扫描该列上的非聚集索引,因为它比聚集索引小得多,而且我确信它只会返回几行
-
但是您使用的是
SELECT *,因此包含每一列的列将(可能)效率更高。否则实例将需要扫描所述非聚集索引,然后执行键查找;这正是你强制索引时它正在做的事情。让数据引擎决定如何获取数据,它知道自己在做什么。 -
我无法更改查询并且它包含 SELECT *,但是带有键查找的非聚集索引扫描仍然比聚集索引扫描快得多。
-
您可能知道只有几行,但 SQL Server 不知道。如果您希望它能够使用适当的索引,请修复查询;使其成为 SARGable。
标签: sql-server sql-execution-plan