SARG基数估计,为什么不进行全扫描?


11

为什么没有全扫描(在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排序规则数据库级别,则它将导致对同一查询的完全扫描。

Answers:


8

比较不同数据类型的值时,SQL Server遵循数据类型优先规则。由于nvarchar的优先级高于varchar,因此SQL Server必须在比较值之前将列数据转换为nvarchar。这意味着在列上应用函数,这将使查询不可修改。

但是,SQL Server会尽力保护您免受错误的侵害,因此它使用Paul White在博客文章“ 动态查找和隐式隐式转换”中描述的技术来查找一系列值,然后与将列值转换为nvarchar,以残差谓词过滤掉所有误报。

如您所注意到的,但是当列的排序规则是SQL排序规则时,这将不起作用。我相信,其原因可以在比较SQL排序规则与Windows排序规则的文章中找到。

基本上,Windows归类对varchar和nvarchar使用相同的算法,其中SQL归类对varchar数据使用不同的算法,而Windows归类对nvarchar数据使用相同的算法。

因此,在Windows排序规则下从varchar转到nvarchar将使用相同的算法,并且SQL Server可以从nvarchar文字生成一系列值,以从varchar SQL排序规则列索引获取行。但是,当varchar列的排序规则是SQL排序规则时,由于使用的算法不同,因此无法进行排序。


更新:

使用Windows和sql排序规则演示varchar列的不同排序顺序。

SQL小提琴

MS SQL Server 2014架构设置

create table T(C varchar(10));

insert into T values('a-b'),('aa'),('ac');

查询1

select C
from T
order by C collate SQL_Latin1_General_CP1_CI_AS;

结果

|   C |
|-----|
| a-b |
|  aa |
|  ac |

查询2

select C
from T
order by C collate Latin1_General_100_CI_AS;

结果

|   C |
|-----|
|  aa |
| a-b |
|  ac |

0

您必须记住,非聚集索引的叶节点由索引页面组成,其中包含聚簇键或RID来定位数据行。

在where子句中,您要声明,VeryRandomText = N'111'因为VeryRandomText上有一个非聚簇索引(除非您明确告诉它创建聚簇,否则create index将创建非聚簇索引)查找数据的最便宜方法是扫描索引以查找rowid,然后然后获取该行的数据。

如果要创建聚簇索引

CREATE clustered Index IX_VeryRandomText On dbo.TestTable (VeryRandomText)

或VeryRandomText上的主键,您将对该索引进行扫描。

在线或在此处查看图书:http : //www.sqlforge.com/w/Clustered_index,_nonclustered_index,_or_heap


是的,我知道你在写什么。如您所见,TestTableID上已经有聚簇索引。但是问题是-如果SQL Server无法查看列数据分布的统计信息(例如,在这种情况下,由于数据类型不匹配,则需要所有行值数据类型转换),因此在这种情况下,应选择“聚集索引扫描”,而不是“索引查找” 。
贾尼斯,2015年

而且,查找/扫描非聚簇索引并不总是最便宜的-当值之间的区别不够明显或存在非覆盖索引时,进行聚簇索引扫描可能会更便宜。
贾尼斯,2015年

@亚尼斯不accoring到你的脚本创建索引不会创建,你必须明确这样说,一个聚集索引-相同的,如果你读的查询计划,索引查找(非聚集)
Spörri

“创建PRIMARY KEY约束时,如果表上的聚簇索引尚不存在并且您未指定唯一的非聚簇索引,则会在一个或多个列上自动创建一个唯一的聚簇索引。” msdn.microsoft.com/en-us/library/ms186342.aspx
亚尼斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.