web-dev-qa-db-ja.com

この列で自動作成された統計が空になるのはなぜですか?

情報

私の質問は、ヒープである適度に大きなテーブル(約40GBのデータ領域)に関するものです
(残念ながら、アプリケーションの所有者はテーブルにクラスター化インデックスを追加できません)

Identity列(ID)に自動作成された統計が作成されましたが、空です。

  • 統計の自動作成と統計の自動更新がオンになっています
  • テーブルで変更が行われました
  • 更新されている他の(自動作成された)統計があります
  • インデックスによって作成された同じ列に別の統計があります(重複)
  • ビルド:12.0.5546

重複する統計が更新されています: enter image description here

実際の質問

私の理解では、まったく同じ列(重複)に2つの統計がある場合でも、すべての統計を使用でき、変更が追跡されるので、なぜこの統計が空のままなのですか?

統計情報

enter image description here

DB stat info

enter image description here

テーブルサイズ

enter image description here

統計が作成される列情報

enter image description here

[ID] [int] IDENTITY(1,1) NOT NULL

ID列

select * from sys.stats  
where name like '%_WA_Sys_0000000A_6B7099F3%';

enter image description here自動作成

別の統計に関する情報を取得しています

select * From sys.dm_db_stats_properties (1802541555, 3)  

enter image description here

私の空の統計と比較して:

enter image description here

「生成スクリプト」からの統計+ヒストグラム:

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

統計のコピーを作成するときに、内部にデータがありません

CREATE STATISTICS [_WA_Sys_0000000A_6B7099F3_TEST] ON [dbo].[table]([ID]) WITH STATS_STREAM = 0x01000000010000000000000000000000EC03686B0000000040000000000000000000000000000000380348063800000004000A00000000000000000000000000

enter image description here

統計を手動で更新すると、更新されます

UPDATE STATISTICS [dbo].[Table]([_WA_Sys_0000000A_6B7099F3_TEST])

enter image description here

8
Randi Vertongen

空の統計情報と入力された統計情報の両方を使用して、これを再現できました。空のテーブルに自動統計が作成されるように手配しましたが、インデックスは後で作成されました。

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');

Output

空ではないすべての重複で変更が引き続き正確に追跡されていることがわかりましたが、(非同期設定に関係なく)自動的に更新される統計は1つだけです。

自動統計更新は、クエリオプティマイザーが特定の統計を必要とし、それが古くなっている(最適性関連の再コンパイル)場合にのみ発生します。

オプティマイザは SQL Server 2012のキャッシュと再コンパイルの計画 ペーパーで説明されているように、重複した統計から選択します。

このドキュメントのトピックに直接関係のない問題は、同じ列セットの同じ順序で複数の統計情報が与えられた場合、クエリオプティマイザーはクエリ最適化中にロードするものをどのように決定するのですか?答えは単純ではありませんが、クエリオプティマイザは次のようなガイドラインを使用します。古い統計よりも最近の統計を優先する。サンプリングを使用して計算されたものよりも、FULLSCANオプションを使用して計算された統計を優先します。等々。

重要なのは、オプティマイザが利用可能な重複統計(「最良のもの」)からoneを選択し、その統計が古くなっている場合は自動的に更新されることです。

これは古いリリースからの動作の変更であると思います。または、少なくともドキュメントでは、オブジェクトのすべての古い統計がこのプロセスの一部として更新されることを示唆していますが、いつ変更されたかはわかりません。 Matt Bowlerが Duplicate Statistics を投稿したのは2013年8月以降でした。これには、便利なAdventureWorksベースのリポジトリが含まれています。このスクリプトにより、統計オブジェクトの1つだけが更新されましたが、当時は両方が更新されていました。

上記の説明は、シナリオを再現しようとしたときに観察したすべての動作と一致しますが、どこかに明示的に記載されているとは思えません。複製を完全に更新しておく価値はほとんどないため、これは賢明な最適化のように見えます。

これはおそらく、Microsoftがサポートしようとしているレベルよりも詳細です。これは、予告なく変更される可能性があることも意味します。

9
Paul White 9