为啥过滤统计信息被忽略

Posted

技术标签:

【中文标题】为啥过滤统计信息被忽略【英文标题】:Why is Filtered Statistics being ignored为什么过滤统计信息被忽略 【发布时间】:2016-03-18 06:02:34 【问题描述】:

我在理解 Cardinality Estimator 如何使用过滤统计信息时遇到了一些问题。

当我在 CE 120 上运行查询时,它会产生预期的估计值,但是当我切换到 CE 70 时,它似乎忽略了过滤后的统计信息。

此外,如果我不使用 WHERE 子句中的实际统计信息列,CE 120 似乎会忽略过滤的统计信息。

以下脚本用于创建和填充测试表以及创建过滤的统计信息。

/*
Microsoft SQL Server 2014 - 12.0.4213.0 (X64) 
    Jun  9 2015 12:06:16 
    Copyright (c) Microsoft Corporation
*/

IF OBJECT_ID('Test_FS') IS NOT NULL  
  DROP TABLE Test_FS;

CREATE TABLE Test_FS (id int identity, a char(2), b char(2), c datetime
CONSTRAINT [Test_FS_pk] PRIMARY KEY CLUSTERED (id ASC))

INSERT INTO Test_FS (a,b,c)
    VALUES 
       ('A1', 'B1', '2016-03-01'),
       ('A1', 'B1', '2016-09-01'),
       ('A1', 'B2', '2016-09-01'),
       ('A1', 'B2', '2016-09-01'),
       ('A1', 'B2', '2016-09-01'),
       ('A1', 'B2', '2016-09-01'),
       ('A1', 'B2', '2016-09-01'),
       ('A1', 'B2', '2016-09-01'),
       ('A2', 'B1', '2016-09-01'),
       ('A2', 'B1', '2016-09-01')

CREATE STATISTICS Test_FS_Filter_Stat 
ON Test_FS (c)
WHERE a = 'A1' 
      AND b = 'B1'
WITH FULLSCAN

第一个查询是CE 120

SELECT *
FROM Test_FS
WHERE a = 'A1'
      AND b = 'B1'
      AND c >= GETDATE()
OPTION(RECOMPILE)

它的行为与预期的一样:估计 1 行 / 实际 1 行

值得注意的是,它不会为个人自动创建统计信息 列

问题 1

当我们使用CE 70 运行它时

SELECT *
FROM Test_FS
WHERE a = 'A1'
      AND b = 'B1'
      AND c >= GETDATE()
OPTION(RECOMPILE, QUERYTRACEON 9481)

它忽略过滤的统计数据并估计 2.88 行。

请注意,我们现在为各个列自动创建了统计信息

统计名称 _WA_Sys_00000002_3587F3E0 _WA_Sys_00000003_3587F3E0 _WA_Sys_00000004_3587F3E0 Test_FS_Filter_Stat

为什么CE 70 会忽略过滤的统计信息?

我感觉是 GETDATE() 造成的,但我不明白为什么以及可以做些什么。

问题 2

现在使用CE 120 对过滤后的统计信息target 列运行不带条件的查询 - 仅在过滤器列上运行条件。

SELECT *
FROM Test_FS
WHERE a = 'A1'
      AND b = 'B1'
OPTION(RECOMPILE)

这次它估计 3.57771 行,实际返回 2。

为什么不使用过滤后的统计数据 - 总行数?


附加信息

我查看了为上述查询加载的统计信息,结果如下:

问题 1: CE 70 在谓词为 '>= GETDATE()' 时忽略过滤统计信息,但在指定 '= GETDATE()' 时使用它。如果指定了日期常数,CE 70 在这两种情况下都使用过滤的统计信息。

CE 120 在上述所有情况下都使用过滤后的统计信息。

问题 2: 当没有在“c”上指定谓词时,两个 CE 都完全忽略了过滤的统计信息,我觉得这很奇怪,因为这肯定会给他们最好的估计。

【问题讨论】:

【参考方案1】:

回答你的第一个问题:GETDATE 是非确定性函数:

DECLARE @c DATETIME = GETDATE()

SELECT *
FROM Test_FS
WHERE a = 'A1' AND b = 'B1' AND c >= @c
OPTION(RECOMPILE)

SELECT *
FROM Test_FS
WHERE a = 'A1' AND b = 'B1' AND c >= @c
OPTION(RECOMPILE, QUERYTRACEON 9481)

来自dbForge的执行计划:

【讨论】:

感谢德瓦特。这就是我陷入困境的地方——这就是我发现的:如果我只有一个条件“c >= GETDATE()”,那么两个 CE 的估计值都是正确的。湾。这是相同的查询,但估计对于 CE 120 是正确的,而不是对于 CE 70? GETDATE() 改变了行为吗?另外,请参阅问题中的其他信息 @carsten Cardinality Estimator 部分记录,所以对你的问题没什么好说的...... :(

以上是关于为啥过滤统计信息被忽略的主要内容,如果未能解决你的问题,请参考以下文章

网站统计与内容填充中所忽略的问题

删除过滤的统计信息会导致死锁

为啥 PyPi 不再显示下载统计信息? [关闭]

什么是首选?创建统计信息或创建非聚集过滤索引?

为啥 boost::mpi::request.test() 返回无效的统计信息

Django-Filter 包:如何过滤对象以创建统计信息而不是列表