为什么忽略过滤统计信息

时间:2016-03-18 06:02:34

标签: sql-server sql-server-2012 statistics sql-server-2014 cardinality

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

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

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

在脚本下面创建并填充测试表以及创建过滤后的统计信息。

/*
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行。

  

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

     

statistics_name
  _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:
当谓词是' > = GETDATE()'时,CE 70会忽略过滤的统计数据。但是当' = GETDATE()'时使用它已指定。如果指定了日期常量,则CE 70在两种情况下都使用过滤统计。

CE 120在上述所有情况下都使用过滤统计数据。

问题2:
当没有在' c'上指定谓词时,CE都完全忽略了Filtered Stats。我觉得很奇怪,因为这应该给他们最好的估计。

1 个答案:

答案 0 :(得分:0)

回答第一个问题: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的执行计划:

enter image description here