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 的统计信息?应该是?
是否有其他选项可以解决此问题?
当您使用局部变量时,过滤的索引和统计信息不会起作用,除非您使用OPTION (RECOMPILE)查询提示,并且运行的是 SQL Server 2008 R2 或更高版本。
Tim Chapman 的 MSDN 博客文章通过示例进行了解释。