由于数据倾斜,嵌套循环的估计值非常低

Fre*_*gen 5 performance sql-server sql-server-2016 cardinality-estimates query-performance

在 SQL Server 2016 SP2 上,我们有一个查询对嵌套循环运算符的估计非常低。由于估计值较低,此查询也会溢出到 tempdb。

如果我是正确的,SQL Server 2014+ 使用粗直方图估计来计算连接的估计行数。
但是当我执行查询时,SQL Server 使用密度向量来计算估计的行数。如果没有子句,
SQL Server 是否仅使用粗直方图估计where

通常,当我有一个包含倾斜数据的表时,我会使用过滤的统计数据来改进估计。但在这种情况下,这似乎不起作用。

有没有办法改进嵌套循环的估计?

使用以下代码可以重现数据:

create table MyTable
(
    id int identity,
    field varchar(50),
    constraint  pk_id primary  key clustered (id)
)
go

create table SkewedTable
(
    id int identity,
    startdate datetime,
    myTableId int,
    remark varchar(50),
    constraint  pk_id primary  key clustered (id)
)

set nocount on

insert into MyTable select top 1000 [name] from master..spt_values
go

insert into SkewedTable select GETDATE(),FLOOR(RAND()*(1000))+1,REPLICATE(N'A',FLOOR(RAND()*(40))+1)
go 1000

insert into SkewedTable select GETDATE(),FLOOR(RAND()*(1000))+1,REPLICATE(N'A',FLOOR(RAND()*(40))+1)
go 

CREATE NONCLUSTERED INDEX [ix_field] ON [dbo].[MyTable]([field] ASC)
go

CREATE NONCLUSTERED INDEX [ix_mytableid] ON [dbo].[SkewedTable]([myTableId] ASC)
go

--95=varchar in sys.messages
set nocount off

;with cte as
( 
    select GETDATE() as startdate ,95 as myTableId, REPLICATE(N'B',FLOOR(RAND()*(40))+1) as remark
    union all
    select * from cte
)
insert into skewedtable select top 40000 * from cte
option(maxrecursion 0)
go

update statistics mytable with fullscan
go

update statistics skewedtable with fullscan
go
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 7

通常,当我有一个包含倾斜数据的表时,我会使用过滤的统计数据来改进估计。但在这种情况下,这似乎不起作用。

您应该会发现以下过滤统计数据很有帮助:

CREATE STATISTICS [stats id (field=varchar)]
ON dbo.MyTable (id)
WHERE field = 'varchar'
WITH FULLSCAN;
Run Code Online (Sandbox Code Playgroud)

这为优化器提供了有关匹配分布的信息,从而为连接提供了更好的选择性估计:id field = 'varchar'

实际执行计划

上面的执行计划显示了过滤统计数据的准确估计,导致优化器选择散列连接(出于成本原因)。

这种分布信息比估计器用来匹配连接直方图(精细或粗略对齐)的精确方法,甚至一般假设(例如简单连接、基本包含)重要得多。

如果您不能这样做,那么您的选择大致如您之前的问题Sort splashs to tempdb 由于 varchar(max) 的回答中所述。我的偏好可能是中间临时表。