为什么没有全扫描(在 SQL 2008 R2 和 2012 上)?
测试数据:
DROP TABLE dbo.TestTable
GO
CREATE TABLE dbo.TestTable
(
TestTableID INT IDENTITY PRIMARY KEY,
VeryRandomText VarChar(50),
VeryRandomText2 VarChar(50)
)
Go
Set NoCount ON
Declare @i int
Set @i = 0
While @i < 10000
Begin
Insert Into dbo.TestTable(VeryRandomText, VeryRandomText2)
Values(Cast(Rand()*10000000 as VarChar(50)), Cast(Rand()*10000000 as VarChar(50)));
Set @i = @i + 1;
End
Go
CREATE Index IX_VeryRandomText On dbo.TestTable
(
VeryRandomText
)
Go
执行查询时:
Select * From dbo.TestTable Where VeryRandomText = N'111' -- bad
收到警告(正如预期的那样,因为将 nchar 数据与 varchar 列进行比较):
<PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(nvarchar(50),[DemoDatabase].[dbo].[TestTable].[VeryRandomText],0)" />
但后来我看到了执行计划,我可以看到,它没有像我预期的那样使用全扫描,而是使用索引搜索。
当然,这有点好,因为在这种特殊情况下,执行速度比完全扫描要快得多。
但我不明白 SQL Server 是如何决定制定这个计划的。
此外,如果服务器排序规则是服务器级别的 Windows 排序规则和 SQL Server 排序规则数据库级别,那么它会导致对同一查询进行全面扫描。
比较不同数据类型的值时,SQL Server 遵循数据类型优先规则。由于 nvarchar 的优先级高于 varchar,SQL Server 必须在比较值之前将列数据转换为 nvarchar。这意味着在列上应用一个函数,这将使查询不可搜索。
然而,SQL Server 确实尽力保护您免受错误的影响,因此它使用 Paul White 在博客文章动态搜索和隐藏隐式转换中描述的技术来搜索一系列值,然后进行最终比较,与在剩余谓词中将列值转换为 nvarchar 以过滤掉任何误报。
正如您所注意到的,当列的排序规则是 SQL 排序规则时,这不起作用。我相信,其原因可以在比较 SQL 排序规则与 Windows 排序规则一文中找到
基本上,Windows 排序规则对 varchar 和 nvarchar 使用相同的算法,而 SQL 排序规则对 varchar 数据使用不同的算法,而对 nvarchar 数据使用与 Windows 排序规则相同的算法。
因此,在 Windows 排序规则下从 varchar 到 nvarchar 将使用相同的算法,并且 SQL Server 可以生成一系列值,在您的情况下,nvarchar 文字从 varchar SQL 排序规则列索引中获取行。但是,当 varchar 列的排序规则是 SQL 排序规则时,由于使用了不同的算法,这是不可能的。
更新:
使用 windows 和 sql 排序规则对 varchar 列的不同排序顺序的演示。
SQL小提琴
MS SQL Server 2014 架构设置:
查询 1:
结果:
查询 2:
结果:
您必须记住,非聚集索引的叶节点由索引页组成,这些索引页包含用于定位数据行的聚集键或 RID。
在你的 where 子句中你声明
VeryRandomText = N'111'
因为 VeryRandomText 上有一个非聚集索引(创建索引将创建非聚集索引,除非你明确告诉它创建一个聚集索引)查找数据的最便宜的方法是扫描索引以找到 rowid 和然后获取该行的数据。如果您要创建聚簇索引
或 VeryRandomText 上的主键,您将扫描该索引。
在线或在此处查看书籍:http ://www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap