为什么此列上的自动创建统计为空?

Ran*_*gen 9 sql-server statistics sql-server-2014

信息

我的问题涉及一个中等大的表(~40GB 数据空间),它是一个堆
(不幸的是,我不允许应用程序所有者向该表添加聚集索引)

已创建 Identity 列 ( ID)上的自动创建统计信息,但该统计信息为空。

  • 自动创建统计数据和自动更新统计数据已开启
  • 表中发生了修改
  • 还有其他(自动创建的)统计信息正在更新
  • 索引创建的同一列上还有另一个统计信息(重复)
  • 版本:12.0.5546

重复统计正在更新: 在此处输入图片说明

实际问题

据我了解,即使在完全相同的列(重复)上有两个统计数据,也可以使用所有统计数据并跟踪修改,那么为什么此统计数据仍为空?

统计信息

在此处输入图片说明

数据库统计信息

在此处输入图片说明

桌子尺寸

在此处输入图片说明

创建统计信息的列信息

在此处输入图片说明

[ID] [int] IDENTITY(1,1) NOT NULL
Run Code Online (Sandbox Code Playgroud)

身份栏

select * from sys.stats  
where name like '%_WA_Sys_0000000A_6B7099F3%';
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明 自动创建

获取有关另一个统计数据的一些信息

select * From sys.dm_db_stats_properties (1802541555, 3)  
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

与我的空数据相比:

在此处输入图片说明

来自“生成脚本”的统计数据 + 直方图:

/****** Object:  Statistic [_WA_Sys_0000000A_6B7099F3]    Script Date: 2/1/2019 10:18:19 AM ******/

    CREATE STATISTICS [_WA_Sys_0000000A_6B7099F3] ON [dbo].[table]([ID]) WITH STATS_STREAM = 0x01000000010000000000000000000000EC03686B0000000040000000000000000000000000000000380348063800000004000A00000000000000000000000000
Run Code Online (Sandbox Code Playgroud)

创建统计数据的副本时,里面没有数据

CREATE STATISTICS [_WA_Sys_0000000A_6B7099F3_TEST] ON [dbo].[table]([ID]) WITH STATS_STREAM = 0x01000000010000000000000000000000EC03686B0000000040000000000000000000000000000000380348063800000004000A00000000000000000000000000
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

手动更新统计信息时,它们确实会更新。

UPDATE STATISTICS [dbo].[Table]([_WA_Sys_0000000A_6B7099F3_TEST])
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

Pau*_*ite 10

我能够通过一个空的统计数据和一个填充的统计数据来重现这一点。我安排在一个空表上创建一个自动统计,后来创建了索引:

IF OBJECT_ID(N'dbo.Heap', N'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.Heap;
END;
GO
CREATE TABLE dbo.Heap 
(
    id integer NOT NULL IDENTITY,
    val integer NOT NULL,
);
GO
-- Add 1000 rows
INSERT dbo.Heap
    WITH (TABLOCKX)
    (val)
SELECT
    SV.number
FROM master.dbo.spt_values AS SV
WHERE
    SV.[type] = N'P'
    AND SV.number BETWEEN 1 AND 1000;
GO
SELECT COUNT_BIG(*) 
FROM dbo.Heap AS H
JOIN dbo.Heap AS H2
    ON H2.id = H.id
WHERE H.id > 0
AND H2.id > 0;
GO
-- Empty table
TRUNCATE TABLE dbo.Heap;
GO
-- Repeat exact same query (RT = 500 + 0.2 * 1000 = 700)
GO
SELECT COUNT_BIG(*) 
FROM dbo.Heap AS H
JOIN dbo.Heap AS H2
    ON H2.id = H.id
WHERE H.id > 0
AND H2.id > 0;
GO
-- Add 1000 rows
INSERT dbo.Heap
    WITH (TABLOCKX)
    (val)
SELECT
    SV.number
FROM master.dbo.spt_values AS SV
WHERE
    SV.[type] = N'P'
    AND SV.number BETWEEN 1 AND 1000;
GO
-- Add index
ALTER TABLE dbo.Heap ADD 
    CONSTRAINT [PK dbo.Heap id]
    PRIMARY KEY NONCLUSTERED (id);
GO
SELECT
    S.[name],
    S.auto_created,
    DDSP.stats_id,
    DDSP.last_updated,
    DDSP.[rows],
    DDSP.rows_sampled,
    DDSP.steps,
    DDSP.unfiltered_rows,
    DDSP.modification_counter
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.[object_id], S.stats_id) AS DDSP
WHERE 
    S.[object_id] = OBJECT_ID(N'dbo.Heap', N'U');
Run Code Online (Sandbox Code Playgroud)

输出

我发现在所有非空重复项上继续准确跟踪修改,但只有一个统计数据会自动更新(无论异步设置如何)。

自动统计更新仅在查询优化器需要特定统计并发现它已过期(与优化相关的重新编译)时发生。

优化器从SQL Server 2012中的计划缓存和重新编译论文中提到的重复统计信息中进行选择:

一个与本文档主题没有直接关系的问题是:在同一组列上以相同的顺序给出多个统计信息,查询优化器如何决定在查询优化期间加载哪些?答案并不简单,但查询优化器使用以下准则: 优先考虑最近的统计信息而不是旧的统计信息;优先选择使用FULLSCAN选项计算的统计数据,而不是使用抽样计算的统计数据;等等。

关键是优化器选择一个可用的重复统计信息(“最好的”),如果发现它是陈旧的,则会自动更新。

我相信这是旧版本的行为变化 - 或者至少文档表明对象的所有过时统计信息都将作为此过程的一部分进行更新,但我不知道何时发生变化。肯定是在 2013 年 8 月之后,Matt Bowler 发布了Duplicate Statistics,其中包含一个方便的基于 AdventureWorks 的存储库。该脚本现在只更新一个统计对象,而当时两者都是。

上面的解释与我在尝试重现您的场景时观察到的所有行为相匹配,但我怀疑它是否在任何地方都有明确记录。这似乎是一个明智的优化,因为保持重复完全更新几乎没有价值。

这可能都低于 Microsoft 愿意支持的详细程度。这也意味着它可能会更改,恕不另行通知。