为什么会忽略额外的过滤统计信息(EAV 模式)?

cro*_*sek 6 sql-server statistics eav cardinality-estimates

我正在尝试改进此子查询(较大查询的)的行估计。估计显示 1266 行。实际是 117k 行。这个特定的属性(EAV 模式)只定义了两个值(2 和 3):

declare @pPropVal smallint = 2;

select Value, ObjectId 
  from Oav.ValueArray PropName
 where PropName.PropertyId = 897
   and PropName.Value  = @pPropVal
option (recompile)
Run Code Online (Sandbox Code Playgroud)

查询计划按预期显示对 PropertyId 和 Value 索引 IX_ValueArray_PropValObj 的正确搜索谓词。

( A ) 作为改进行估计的尝试,添加了一个额外的统计数据,使行估计略微增加到 3041:

create statistics [ST_SomePropertyName] ON [Oav].[ValueArray](PropertyId, Value, ObjectId)
 where 
     (     
             PropertyId = 897 
         and [Value] is not null
     )
  with fullscan
Run Code Online (Sandbox Code Playgroud)

直方图显示单行。HI 键只是 PropertyId(第一列),根据我的理解,它不是那么有用,它使用的是密度信息。

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS  DISTINCT_RANGE_ROWS  AVG_RANGE_ROWS
897             0           196026   0                    1

All density Average Length  Columns
1           4               PropertyId
0.5         8               PropertyId, Value

Name    Updated Rows    Rows Sampled    Steps   Density Average key length  String Index    Filter Expression   Unfiltered Rows
ST_SomePropertyName May 20 2014  2:01PM 196026  196026  1   0   8   NO  ([PropertyId]=(897) AND [Value] IS NOT NULL)    9317055
Run Code Online (Sandbox Code Playgroud)

( B ) 由于 PropertyId = 897 有一个过滤器,我想我可以像这样重新创建统计信息:

create statistics [ST_SomePropertyName] ON [Oav].[ValueArray](Value, ObjectId)
where
    (       
       PropertyId = 897 
       and [Value] is not null
    )
 with fullscan
Run Code Online (Sandbox Code Playgroud)

直方图在我看来很有用,但估计器似乎忽略了它,因为它恢复到 1266 的原始估计值。

RANGE_HI_KEY  RANGE_ROWS  EQ_ROWS  DISTINCT_RANGE_ROWS   AVG_RANGE_ROWS
2             0           117760   0                     1
3             0           78266    0                     1

All density   Average Length  Columns
0.5           4               Value
5.101364E-06  12              Value, ObjectId

Name    Updated Rows    Rows Sampled    Steps   Density Average key length  String Index    Filter Expression   Unfiltered Rows
ST_SomePropertyName May 20 2014  2:04PM 196026  196026  2   0   12  NO  ([PropertyId]=(897) AND [Value] IS NOT NULL)    9317055
Run Code Online (Sandbox Code Playgroud)

( C ) 过滤到固定值确实有效(甚至不需要第二两列),但这不是一个非常实用的解决方案。这给出了准确的估计 117k。

create statistics [ST_SomePropertyName] ON [Oav].[ValueArray](PropertyId)
 where 
     (     
             PropertyId = 897 
         and [Value] = 2
     )
  with fullscan
Run Code Online (Sandbox Code Playgroud)

直方图:

RANGE_HI_KEY   RANGE_ROWS  EQ_ROWS  DISTINCT_RANGE_ROWS  AVG_RANGE_ROWS
897            0           117760   0                    1
Run Code Online (Sandbox Code Playgroud)

D)(添加到原始问题)将值限制在较小范围内的方法有帮助。但是,如果范围值不统一或者值是基于字符串的字段或什至不知道,这通常可能不是一个好的解决方法:

CREATE STATISTICS [ST_ListUnderBrand_897] ON [Oav].[ValueArray](PropertyId, Value)
WHERE 
  (       
      PropertyId = 897 
      and [Value] >= 1 and [Value] <= 20
  )
  with fullscan
Run Code Online (Sandbox Code Playgroud)

这给出了大约 16k 的估计。将 [1,20] 更改为精确的 [2,3] 给出了大约 80k 的估计值。很明显,表数据中值的真实范围并未真正使用(因为它是第二列),这是主要基于过滤器范围的一些估计。

请注意 Value 字段是一个 sql_variant 但我认为这不相关,因为查询计划不显示任何隐式转换。

为什么 SQL Server 不使用来自 B 的统计信息?应该是?

是否有其他选项可以解决此问题?

Bre*_*zar 6

当您使用局部变量时,过滤的索引和统计信息不会起作用,除非您使用OPTION (RECOMPILE)查询提示,并且运行的是 SQL Server 2008 R2 或更高版本。

Tim Chapman 的 MSDN 博客文章通过示例进行了解释。