Saya akan membatasi posting ini untuk membahas statistik satu kolom karena sudah cukup panjang dan Anda tertarik pada bagaimana SQL Server memasukkan data ke dalam langkah-langkah histogram. Untuk statistik multi-kolom histogram hanya dibuat pada kolom terkemuka.
Ketika SQL Server menentukan bahwa pembaruan statistik diperlukan itu memulai permintaan tersembunyi yang membaca semua data tabel atau sampel data tabel. Anda dapat melihat pertanyaan ini dengan acara yang diperpanjang. Ada fungsi yang disebut StatMan
dalam SQL Server yang terlibat dengan membuat histogram. Untuk objek statistik sederhana setidaknya ada dua jenis StatMan
kueri yang berbeda (ada kueri yang berbeda untuk pembaruan stat cepat dan saya menduga bahwa fitur statistik tambahan pada tabel yang dipartisi juga menggunakan kueri yang berbeda).
Yang pertama hanya mengambil semua data dari tabel tanpa penyaringan. Anda dapat melihat ini ketika tabel sangat kecil atau Anda mengumpulkan statistik dengan FULLSCAN
opsi:
CREATE TABLE X_SHOW_ME_STATMAN (N INT);
CREATE STATISTICS X_STAT_X_SHOW_ME_STATMAN ON X_SHOW_ME_STATMAN (N);
-- after gathering stats with 1 row in table
SELECT StatMan([SC0]) FROM
(
SELECT TOP 100 PERCENT [N] AS [SC0]
FROM [dbo].[X_SHOW_ME_STATMAN] WITH (READUNCOMMITTED)
ORDER BY [SC0]
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 16);
SQL Server mengambil ukuran sampel otomatis berdasarkan ukuran tabel (saya pikir itu adalah jumlah baris dan halaman dalam tabel). Jika tabel terlalu besar maka ukuran sampel otomatis turun di bawah 100%. Inilah yang saya dapatkan untuk tabel yang sama dengan baris 1M:
-- after gathering stats with 1 M rows in table
SELECT StatMan([SC0], [SB0000]) FROM
(
SELECT TOP 100 PERCENT [SC0], step_direction([SC0]) over (order by NULL) AS [SB0000]
FROM
(
SELECT [N] AS [SC0]
FROM [dbo].[X_SHOW_ME_STATMAN] TABLESAMPLE SYSTEM (6.666667e+001 PERCENT) WITH (READUNCOMMITTED)
) AS _MS_UPDSTATS_TBL_HELPER
ORDER BY [SC0], [SB0000]
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1);
TABLESAMPLE
adalah didokumentasikan tetapi Statman dan step_direction tidak. di sini SQL Server sampel sekitar 66,6% dari data dari tabel untuk membuat histogram. Artinya, Anda bisa mendapatkan jumlah langkah histogram yang berbeda saat memperbarui statistik (tanpa FULLSCAN
) pada data yang sama. Saya belum pernah mengamati ini dalam praktik tetapi saya tidak melihat mengapa itu tidak mungkin.
Mari kita jalankan beberapa tes pada data sederhana untuk melihat bagaimana statistik berubah seiring waktu. Di bawah ini adalah beberapa kode tes yang saya tulis untuk memasukkan bilangan bulat berurutan ke dalam tabel, mengumpulkan statistik setelah setiap sisipan, dan menyimpan informasi tentang statistik ke dalam tabel hasil. Mari kita mulai dengan memasukkan 1 baris sekaligus hingga 10.000. Test bed:
DECLARE
@stats_id INT,
@table_object_id INT,
@rows_per_loop INT = 1,
@num_of_loops INT = 10000,
@loop_num INT;
BEGIN
SET NOCOUNT ON;
TRUNCATE TABLE X_STATS_RESULTS;
SET @table_object_id = OBJECT_ID ('X_SEQ_NUM');
SELECT @stats_id = stats_id FROM sys.stats
WHERE OBJECT_ID = @table_object_id
AND name = 'X_STATS_SEQ_INT_FULL';
SET @loop_num = 0;
WHILE @loop_num < @num_of_loops
BEGIN
SET @loop_num = @loop_num + 1;
INSERT INTO X_SEQ_NUM WITH (TABLOCK)
SELECT @rows_per_loop * (@loop_num - 1) + N FROM dbo.GetNums(@rows_per_loop);
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN; -- can comment out FULLSCAN as needed
INSERT INTO X_STATS_RESULTS WITH (TABLOCK)
SELECT 'X_STATS_SEQ_INT_FULL', @rows_per_loop * @loop_num, rows_sampled, steps
FROM sys.dm_db_stats_properties(@table_object_id, @stats_id);
END;
END;
Untuk data ini jumlah langkah histogram dengan cepat meningkat menjadi 200 (pertama-tama menyentuh jumlah maksimum langkah dengan 397 baris), tetap pada 199 atau 200 hingga 1485 baris ada di tabel, kemudian perlahan-lahan menurun hingga histogram hanya memiliki 3 atau 4 Langkah. Berikut adalah grafik dari semua data:
Begini histogramnya untuk baris 10rb:
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1 0 1 0 1
9999 9997 1 9997 1
10000 0 1 0 1
Apakah masalah bahwa histogram hanya memiliki 3 langkah? Sepertinya informasi dipertahankan dari sudut pandang kami. Perhatikan bahwa karena datatype adalah INTEGER kita bisa mengetahui berapa banyak baris dalam tabel untuk setiap bilangan bulat dari 1 - 10.000. Biasanya SQL Server juga bisa mengetahuinya, meskipun ada beberapa kasus di mana ini tidak cukup berhasil . Lihat posting SE ini untuk contohnya.
Menurut Anda apa yang akan terjadi jika kami menghapus satu baris dari tabel dan memperbarui statistik? Idealnya kita akan mendapatkan langkah histogram lain untuk menunjukkan bahwa integer yang hilang tidak lagi ada dalam tabel.
DELETE FROM X_SEQ_NUM
WHERE X_NUM = 1000;
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps
DELETE FROM X_SEQ_NUM
WHERE X_NUM IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps
Itu sedikit mengecewakan. Jika kami membuat histogram dengan tangan, kami akan menambahkan langkah untuk setiap nilai yang hilang. SQL Server menggunakan algoritme tujuan umum sehingga untuk beberapa set data, kami mungkin dapat membuat histogram yang lebih cocok daripada kode yang digunakannya. Tentu saja, perbedaan praktis antara mendapatkan 0 atau 1 baris dari tabel sangat kecil. Saya mendapatkan hasil yang sama ketika menguji dengan 20.000 baris yang masing-masing bilangan bulat memiliki 2 baris dalam tabel. Histogram tidak mendapatkan langkah saat saya menghapus data.
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1 0 2 0 1
9999 19994 2 9997 2
10000 0 2 0 1
Jika saya menguji dengan 1 juta baris dengan setiap bilangan bulat memiliki 100 baris dalam tabel, saya mendapatkan hasil yang sedikit lebih baik, tetapi saya masih dapat membuat histogram yang lebih baik dengan tangan.
truncate table X_SEQ_NUM;
BEGIN TRANSACTION;
INSERT INTO X_SEQ_NUM WITH (TABLOCK)
SELECT N FROM dbo.GetNums(10000);
GO 100
COMMIT TRANSACTION;
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- 4 steps
DELETE FROM X_SEQ_NUM
WHERE X_NUM = 1000;
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- now 5 steps with a RANGE_HI_KEY of 998 (?)
DELETE FROM X_SEQ_NUM
WHERE X_NUM IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);
UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;
DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 5 steps
Histogram terakhir:
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1 0 100 0 1
998 99600 100 996 100
3983 298100 100 2981 100
9999 600900 100 6009 100
10000 0 100 0 1
Mari kita uji lebih lanjut dengan integer berurutan tetapi dengan lebih banyak baris dalam tabel. Perhatikan bahwa untuk tabel yang terlalu kecil secara manual menentukan ukuran sampel tidak akan berpengaruh, jadi saya akan menambahkan 100 baris di setiap sisipan dan mengumpulkan statistik setiap kali hingga 1 juta baris. Saya melihat pola yang sama seperti sebelumnya, kecuali sekali saya mendapatkan 637300 baris dalam tabel saya tidak lagi sampel 100% dari baris dalam tabel dengan tingkat sampel default. Saat saya mendapatkan baris, jumlah langkah histogram meningkat. Mungkin ini karena SQL Server berakhir dengan lebih banyak kesenjangan dalam data karena jumlah baris yang tidak dicampuri dalam tabel meningkat. Saya tidak mencapai 200 langkah bahkan pada baris 1 M, tetapi jika saya terus menambahkan baris saya berharap saya akan sampai di sana dan akhirnya mulai turun kembali.
Sumbu X adalah jumlah baris dalam tabel. Ketika jumlah baris bertambah, baris-baris yang diambil sampel sedikit bervariasi dan tidak melebihi 650 ribu.
Sekarang mari kita lakukan beberapa tes sederhana dengan data VARCHAR.
CREATE TABLE X_SEQ_STR (X_STR VARCHAR(5));
CREATE STATISTICS X_SEQ_STR ON X_SEQ_STR(X_STR);
Di sini saya memasukkan 200 angka (sebagai string) bersama dengan NULL.
INSERT INTO X_SEQ_STR
SELECT N FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;
UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;
DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 111 steps, RANGE_ROWS is 0 or 1 for all steps
Perhatikan bahwa NULL selalu mendapatkan langkah histogram sendiri ketika ditemukan dalam tabel. SQL Server bisa memberi saya tepat 201 langkah untuk menyimpan semua informasi tetapi tidak melakukan itu. Informasi teknis hilang karena '1111' antara '1' dan '2' misalnya.
Sekarang mari kita coba memasukkan karakter yang berbeda dan bukan hanya bilangan bulat:
truncate table X_SEQ_STR;
INSERT INTO X_SEQ_STR
SELECT CHAR(10 + N) FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;
UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;
DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 95 steps, RANGE_ROWS is 0 or 1 or 2
Tidak ada perbedaan nyata dari tes terakhir.
Sekarang mari kita coba memasukkan karakter tetapi menempatkan angka yang berbeda dari setiap karakter di dalam tabel. Misalnya, CHAR(11)
memiliki 1 baris, CHAR(12)
memiliki 2 baris, dll.
truncate table X_SEQ_STR;
DECLARE
@loop_num INT;
BEGIN
SET NOCOUNT ON;
SET @loop_num = 0;
WHILE @loop_num < 200
BEGIN
SET @loop_num = @loop_num + 1;
INSERT INTO X_SEQ_STR WITH (TABLOCK)
SELECT CHAR(10 + @loop_num) FROM dbo.GetNums(@loop_num);
END;
END;
UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;
DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 148 steps, most with RANGE_ROWS of 0
Seperti sebelumnya saya masih belum mendapatkan persis 200 langkah histogram. Namun, banyak langkah memiliki RANGE_ROWS
0.
Untuk tes terakhir, saya akan memasukkan string acak 5 karakter di setiap loop dan mengumpulkan statistik setiap kali. Berikut kode string acak:
char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))
Berikut ini adalah grafik baris dalam tabel vs langkah-langkah histogram:
Perhatikan bahwa jumlah langkah tidak turun di bawah 100 setelah mulai naik dan turun. Saya telah mendengar dari suatu tempat (tetapi tidak dapat sumber sekarang) bahwa algoritma membangun histogram SQL Server menggabungkan langkah-langkah histogram karena kehabisan ruang untuk mereka. Jadi Anda bisa berakhir dengan perubahan drastis dalam jumlah langkah hanya dengan menambahkan sedikit data. Inilah satu contoh data yang menurut saya menarik:
ROWS_IN_TABLE ROWS_SAMPLED STEPS
36661 36661 133
36662 36662 143
36663 36663 143
36664 36664 141
36665 36665 138
Bahkan ketika pengambilan sampel dengan FULLSCAN
, menambahkan satu baris dapat meningkatkan jumlah langkah dengan 10, tetap konstan, lalu menguranginya 2, lalu menguranginya dengan 3.
Apa yang bisa kita simpulkan dari semua ini? Saya tidak bisa membuktikan semua ini, tetapi pengamatan ini tampaknya benar:
- SQL Server menggunakan algoritma penggunaan umum untuk membuat histogram. Untuk beberapa distribusi data, dimungkinkan untuk membuat representasi data yang lebih lengkap dengan tangan.
- Jika ada data NULL dalam tabel dan permintaan statistik menemukannya maka data NULL selalu mendapatkan langkah histogram sendiri.
- Nilai minimum yang ditemukan dalam tabel mendapatkan langkah histogram sendiri dengan
RANGE_ROWS
= 0.
- Nilai maksimum yang ditemukan di tabel akan menjadi final
RANGE_HI_KEY
dalam tabel.
- Sebagai SQL Server sampel lebih banyak data, mungkin perlu menggabungkan langkah-langkah yang ada untuk memberikan ruang bagi data baru yang ditemukannya. Jika Anda melihat histogram yang cukup Anda mungkin melihat nilai-nilai umum diulang untuk
DISTINCT_RANGE_ROWS
atau RANGE_ROWS
. Misalnya, 255 muncul beberapa kali untuk RANGE_ROWS
dan DISTINCT_RANGE_ROWS
untuk ujian akhir di sini.
- Untuk distribusi data sederhana, Anda mungkin melihat SQL Server menggabungkan data sekuensial ke dalam satu langkah histogram yang tidak menyebabkan hilangnya informasi. Namun saat menambahkan kesenjangan ke data, histogram mungkin tidak menyesuaikan dengan cara yang Anda harapkan.
Kapan semua ini menjadi masalah? Ini masalah ketika kueri berkinerja buruk karena histogram yang tidak dapat mewakili distribusi data dengan cara pengoptimal kueri untuk membuat keputusan yang baik. Saya pikir ada kecenderungan untuk berpikir bahwa memiliki lebih banyak langkah-langkah histogram selalu lebih baik dan untuk itu ada kekhawatiran ketika SQL Server menghasilkan histogram pada jutaan baris atau lebih tetapi tidak menggunakan langkah-langkah tepat 200 atau 201 histogram. Namun, saya telah melihat banyak masalah statistik bahkan ketika histogram memiliki 200 atau 201 langkah. Kami tidak memiliki kendali atas berapa banyak langkah histogram yang dihasilkan SQL Server untuk objek statistik jadi saya tidak akan khawatir tentang hal itu. Namun, ada beberapa langkah yang bisa Anda ambil ketika Anda mengalami kueri berkinerja buruk yang disebabkan oleh masalah statistik. Saya akan memberikan gambaran yang sangat singkat.
Mengumpulkan statistik secara penuh dapat membantu dalam beberapa kasus. Untuk tabel yang sangat besar ukuran sampel otomatis mungkin kurang dari 1% dari baris dalam tabel. Terkadang hal itu dapat menyebabkan rencana yang buruk tergantung pada gangguan data di kolom. Dokumentasi Microsoft untuk CREATE STATISTICS dan UPDATE STATISTICS mengatakan:
SAMPLE berguna untuk kasus khusus di mana rencana kueri, berdasarkan pengambilan sampel default, tidak optimal. Dalam sebagian besar situasi, tidak perlu menentukan SAMPLE karena pengoptimal permintaan sudah menggunakan pengambilan sampel dan menentukan ukuran sampel yang signifikan secara statistik secara default, seperti yang diperlukan untuk membuat rencana kueri berkualitas tinggi.
Untuk sebagian besar beban kerja, pemindaian penuh tidak diperlukan, dan pengambilan sampel standar memadai. Namun, beban kerja tertentu yang sensitif terhadap distribusi data yang bervariasi mungkin memerlukan ukuran sampel yang meningkat, atau bahkan pemindaian penuh.
Dalam beberapa kasus, membuat statistik yang difilter dapat membantu. Anda mungkin memiliki kolom dengan data miring dan banyak nilai berbeda yang berbeda. Jika ada nilai tertentu dalam data yang biasanya difilter pada Anda dapat membuat histogram statistik hanya untuk nilai-nilai umum tersebut. Pengoptimal kueri dapat menggunakan statistik yang ditentukan pada rentang data yang lebih kecil daripada statistik yang ditentukan pada semua nilai kolom. Anda masih tidak dijamin untuk mendapatkan 200 langkah dalam histogram, tetapi jika Anda membuat statistik yang difilter hanya pada satu nilai Anda akan langkah histogram nilai itu.
Menggunakan tampilan yang dipartisi adalah salah satu cara untuk secara efektif mendapatkan lebih dari 200 langkah untuk tabel. Misalkan Anda dapat dengan mudah membagi meja besar menjadi satu meja per tahun. Anda membuat UNION ALL
tampilan yang menggabungkan semua tabel tahunan. Setiap tabel akan memiliki histogram sendiri. Perhatikan bahwa statistik inkremental baru yang diperkenalkan di SQL Server 2014 hanya memungkinkan pembaruan statistik menjadi lebih efisien. Pengoptimal kueri tidak akan menggunakan statistik yang dibuat per partisi.
Ada banyak lagi tes yang dapat dijalankan di sini, jadi saya mendorong Anda untuk bereksperimen. Saya melakukan pengujian ini pada SQL Server 2014 express sehingga benar-benar tidak ada yang menghentikan Anda.