Apakah indeks ulang memperbarui statistik?

43

Saya telah melakukan kursus MS10775A minggu lalu dan satu pertanyaan yang muncul bahwa pelatih tidak dapat menjawab dengan andal adalah:

Apakah indeks ulang memperbarui statistik?

Kami menemukan diskusi online dengan alasan keduanya memang ada dan tidak.

Thor Erik
sumber
Penting untuk dicatat bahwa REINDEXstatistik kolom yang diperbarui sebagai efek samping dari membangun kembali indeks - Anda tidak perlu memperbarui statistik. Data dalam tabel tidak berubah. Ini adalah data yang sama, hanya a) memindahkan lokasinya pada pelat pemintalan (ketika halaman direorganisasi), atau b) duduk di halaman yang berbeda (dalam kasus pembangunan kembali). Jadi: a re-indeks tidak update (beberapa) statistik: tidak ada kebutuhan untuk melakukannya.
Ian Boyd

Jawaban:

51

Anda dapat mengingat hal-hal berikut ketika peduli tentang memperbarui statistik (disalin dari Rebuilding Indexes vs. Memperbarui Statistik (Benjamin Nevarez)

  1. Secara default, UPDATE STATISTICSpernyataan hanya menggunakan sampel rekaman dari tabel. Menggunakan UPDATE STATISTICS WITH FULLSCANakan memindai seluruh tabel.

  2. Secara default, UPDATE STATISTICSpernyataan memperbarui statistik indeks dan kolom. Menggunakan COLUMNSopsi akan memperbarui statistik kolom saja. Menggunakan INDEXopsi hanya akan memperbarui statistik indeks.

  3. Membangun kembali indeks , misalnya dengan menggunakan ALTER INDEX … REBUILDjuga akan memperbarui statistik indeks dengan setara dengan menggunakan WITH FULLSCAN kecuali jika tabel dipartisi, dalam hal ini statistik hanya sampel (berlaku untuk SQL Server 2012 dan yang lebih baru).

  4. Statistik yang dibuat secara manual menggunakan CREATE STATISTICStidak diperbarui oleh ALTER INDEX ... REBUILDoperasi apa pun , termasuk ALTER TABLE ... REBUILD. ALTER TABLE ... REBUILDtidak memperbarui statistik untuk indeks berkerumun, jika ada yang didefinisikan pada tabel yang sedang dibangun kembali.

  5. Mengatur ulang indeks , misalnya menggunakan ALTER INDEX … REORGANIZEtidak memperbarui statistik apa pun.

Jawaban singkatnya adalah bahwa Anda perlu menggunakan UPDATE STATISTICSuntuk memperbarui statistik kolom dan bahwa pembangunan kembali indeks hanya akan memperbarui statistik indeks. Anda dapat memaksa pembaruan ke semua statistik di atas meja, termasuk statistik-indeks dan statistik yang dibuat secara manual, dengan UPDATE STATISTICS (tablename) WITH FULLSCAN;sintaksis.

Kode berikut menggambarkan aturan yang diuraikan di atas:

Pertama, kita akan membuat tabel dengan beberapa kolom, dan indeks berkerumun:

USE tempdb;

IF OBJECT_ID(N'dbo.SomeTable', N'U') IS NOT NULL
DROP TABLE dbo.SomeTable;

CREATE TABLE dbo.SomeTable
(
    rn int NOT NULL IDENTITY(1,1)
        CONSTRAINT pk
        PRIMARY KEY NONCLUSTERED
    , i int NOT NULL INDEX i 
    , d sysname NOT NULL
) ON [PRIMARY] WITH (DATA_COMPRESSION = NONE);

CREATE UNIQUE CLUSTERED INDEX cx ON dbo.SomeTable (i, d);

CREATE STATISTICS d ON dbo.SomeTable (d) WITH FULLSCAN;

INSERT INTO dbo.SomeTable (d, i)
SELECT c1.name, c1.id
FROM sys.syscolumns c1;

Kueri ini menunjukkan tanggal ketika setiap objek statistik terakhir diperbarui:

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';

Hasilnya menunjukkan belum ada pembaruan yang terjadi, yang benar karena kami baru saja membuat tabel:

╔═══════════════╦═══════════╦═══════════╗
║ ObjectName ║ StatsName ║ StatsDate ║
╠═══════════════╬═══════════╬═══════════╣
║ dbo.SomeTable ║ cx ║ NULL ║
║ dbo.SomeTable ║ i ║ NULL ║
║ dbo.SomeTable ║ pk ║ NULL ║
║ dbo.SomeTable ║ d ║ NULL ║
╚═══════════════╩═══════════╩═══════════╝

Mari kita membangun kembali seluruh tabel, dan melihat apakah itu memperbarui statistik:

ALTER TABLE dbo.SomeTable REBUILD;

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';
╔═══════════════╦═══════════╦═════════════════════ ════╗
║ ObjectName ║ StatsName ║ StatsDate ║
╠═══════════════╬═══════════╬═════════════════════ ════╣
║ dbo.SomeTable ║ cx ║ 2018-09-17 14: 09: 13.590 ║
║ dbo.SomeTable ║ i ║ NULL ║
║ dbo.SomeTable ║ pk ║ NULL ║
║ dbo.SomeTable ║ d ║ NULL ║
╚═══════════════╩═══════════╩═════════════════════ ════╝

Hasilnya menunjukkan hanya statistik indeks yang dikelompokkan yang diperbarui.

Selanjutnya, kami melakukan UPDATE STATSoperasi diskrit :

UPDATE STATISTICS dbo.SomeTable(d) WITH FULLSCAN;

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';

Seperti yang Anda lihat, kami baru saja memperbarui statistik pada dkolom:

╔═══════════════╦═══════════╦═════════════════════ ════╗
║ ObjectName ║ StatsName ║ StatsDate ║
╠═══════════════╬═══════════╬═════════════════════ ════╣
║ dbo.SomeTable ║ cx ║ 2018-09-17 14: 09: 13.590 ║
║ dbo.SomeTable ║ i ║ NULL ║
║ dbo.SomeTable ║ pk ║ NULL ║
║ dbo.SomeTable ║ d ║ 2018-09-17 14: 09: 13.597 ║
╚═══════════════╩═══════════╩═════════════════════ ════╝

Sekarang, kami akan memperbarui statistik di seluruh tabel:

UPDATE STATISTICS dbo.SomeTable WITH FULLSCAN;

SELECT ObjectName = sc.name + N'.' + o.name
    , StatsName = s.name
    , StatsDate = STATS_DATE(s.object_id, s.stats_id)
FROM sys.stats s
    INNER JOIN sys.objects o ON s.object_id = o.object_id
    INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE sc.name = N'dbo'
    AND o.name = N'SomeTable';
╔═══════════════╦═══════════╦═════════════════════ ════╗
║ ObjectName ║ StatsName ║ StatsDate ║
╠═══════════════╬═══════════╬═════════════════════ ════╣
║ dbo.SomeTable ║ cx ║ 2018-09-17 14: 09: 13.600 ║
║ dbo.SomeTable ║ i ║ 2018-09-17 14: 09: 13.600 ║
║ dbo.SomeTable ║ pk ║ 2018-09-17 14: 09: 13.603 ║
║ dbo.SomeTable ║ d ║ 2018-09-17 14: 09: 13.607 ║
╚═══════════════╩═══════════╩═════════════════════ ════╝

Seperti yang Anda lihat, satu-satunya cara untuk memastikan semua statistik diperbarui adalah dengan memperbarui masing-masing secara manual, atau memperbarui seluruh tabel dengan UPDATE STATISTICS (table);.

MicSim
sumber
@ JeremyWeir - seperti yang Anda lihat dari contoh kode yang baru saja saya tambahkan ke pertanyaan di atas, satu-satunya statistik yang diperbarui adalah yang diperbarui secara eksplisit baik melalui pernyataan ALTER INDEX ... REBUILDatau UPDATE STATISTICSpernyataan. Jika tabel itu sendiri dibangun kembali, hanya statistik indeks berkerumun diperbarui. FYI, kunci utama dan indeks berkerumun tidak selalu didukung oleh objek indeks yang sama.
Max Vernon
5

Halaman Microsoft Documents untuk statistik SQL Server menyatakan :

Operasi seperti membangun kembali, defragmenting, atau mengatur ulang indeks tidak mengubah distribusi data. Oleh karena itu, Anda tidak perlu memperbarui statistik setelah menjalankan ALTER INDEX REBUILD, DBCC DBREINDEX, DBCC INDEXDEFRAG, atau operasi ALTER INDEX REORGANIZE . Query Optimizer memperbarui statistik ketika Anda membangun kembali indeks di atas meja atau tampilan dengan ALTER INDEX REBUILD atau DBCC DBREINDEX, namun pembaruan statistik ini adalah produk sampingan dari pembuatan kembali indeks. Query Optimizer tidak memperbarui statistik setelah operasi DBCC INDEXDEFRAG atau ALTER INDEX REORGANIZE.

bside
sumber