Latar Belakang
Data untuk objek statistik dikumpulkan menggunakan pernyataan formulir:
SELECT
StatMan([SC0], [SC1], [SB0000])
FROM
(
SELECT TOP 100 PERCENT
[SC0], [SC1], STEP_DIRECTION([SC0]) OVER (ORDER BY NULL) AS [SB0000]
FROM
(
SELECT
[TextValue] AS [SC0],
[Id] AS [SC1]
FROM [dbo].[Test]
TABLESAMPLE SYSTEM (2.223684e+001 PERCENT)
WITH (READUNCOMMITTED)
) AS _MS_UPDSTATS_TBL_HELPER
ORDER BY
[SC0],
[SC1],
[SB0000]
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1)
Anda dapat mengumpulkan pernyataan ini dengan Extended Events atau Profiler ( SP:StmtCompleted
).
Kueri pembuatan statistik sering mengakses tabel dasar (bukan indeks yang tidak dikelompokkan) untuk menghindari pengelompokan nilai yang secara alami terjadi pada halaman indeks yang tidak dikluster.
Jumlah baris sampel tergantung pada jumlah seluruh halaman yang dipilih untuk pengambilan sampel. Setiap halaman dari tabel dipilih atau tidak. Semua baris pada halaman yang dipilih berkontribusi pada statistik.
Angka acak
SQL Server menggunakan generator nomor acak untuk memutuskan apakah suatu halaman memenuhi syarat atau tidak. Generator yang digunakan dalam contoh ini adalah generator bilangan acak Lehmer dengan nilai parameter seperti yang ditunjukkan di bawah ini:
X selanjutnya = X seed * 7 5 mod (2 31 - 1)
Nilai dihitung sebagai jumlah dari:Xseed
Bagian integer rendah dari bigint
tabel dasar ( ) partition_id
misalnya
SELECT
P.[partition_id] & 0xFFFFFFFF
FROM sys.partitions AS P
WHERE
P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
AND P.index_id = 1;
Nilai yang ditentukan dalam REPEATABLE
klausa
- Untuk sampel
UPDATE STATISTICS
, REPEATABLE
nilainya 1.
- Nilai ini terpapar dalam
m_randomSeed
elemen informasi debugging internal metode akses yang ditunjukkan dalam rencana eksekusi ketika jejak jejak 8666 diaktifkan, misalnya<Field FieldName="m_randomSeed" FieldValue="1" />
Untuk SQL Server 2012, perhitungan ini terjadi di sqlmin!UnOrderPageScanner::StartScan
:
mov edx,dword ptr [rcx+30h]
add edx,dword ptr [rcx+2Ch]
di mana memori at [rcx+30h]
berisi 32 bit id partisi dan memori at [rcx+2Ch]
berisi REPEATABLE
nilai yang digunakan.
Generator nomor acak diinisialisasi kemudian dalam metode yang sama, memanggil sqlmin!RandomNumGenerator::Init
, di mana instruksi:
imul r9d,r9d,41A7h
... mengalikan benih dengan 41A7
hex (16807 desimal = 7 5 ) seperti yang ditunjukkan pada persamaan di atas.
Kemudian angka acak (untuk masing-masing halaman) dihasilkan dengan menggunakan kode dasar yang sama yang diuraikan dalam sqlmin!UnOrderPageScanner::SetupSubScanner
.
StatMan
Untuk StatMan
kueri contoh yang ditunjukkan di atas, halaman yang sama akan dikumpulkan seperti untuk pernyataan T-SQL:
SELECT
COUNT_BIG(*)
FROM dbo.Test AS T
TABLESAMPLE SYSTEM (2.223684e+001 PERCENT) -- Same sample %
REPEATABLE (1) -- Always 1 for statman
WITH (INDEX(0)); -- Scan base object
Ini akan cocok dengan output dari:
SELECT
DDSP.rows_sampled
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.Test', N'U')
AND S.[name] = N'IX_Test_TextValue';
Kasing tepi
Salah satu konsekuensi dari menggunakan generator angka acak MINSTD Lehmer adalah bahwa nilai seed nol dan int.max tidak boleh digunakan karena ini akan menghasilkan algoritma yang menghasilkan urutan nol (memilih setiap halaman).
Kode mendeteksi nol, dan menggunakan nilai dari 'jam' sistem sebagai seed dalam kasus itu. Itu tidak melakukan hal yang sama jika seed adalah int.max ( 0x7FFFFFFF
= 2 31 - 1).
Kita dapat merekayasa skenario ini karena seed awal dihitung sebagai jumlah dari 32 bit yang rendah dari id partisi dan REPEATABLE
nilainya. The REPEATABLE
nilai yang akan menghasilkan benih yang int.max dan karena itu setiap halaman yang dipilih untuk sampel adalah:
SELECT
0x7FFFFFFF - (P.[partition_id] & 0xFFFFFFFF)
FROM sys.partitions AS P
WHERE
P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
AND P.index_id = 1;
Mengerjakan itu menjadi contoh lengkap:
DECLARE @SQL nvarchar(4000) =
N'
SELECT
COUNT_BIG(*)
FROM dbo.Test AS T
TABLESAMPLE (0 PERCENT)
REPEATABLE (' +
(
SELECT TOP (1)
CONVERT(nvarchar(11), 0x7FFFFFFF - P.[partition_id] & 0xFFFFFFFF)
FROM sys.partitions AS P
WHERE
P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
AND P.index_id = 1
) + ')
WITH (INDEX(0));';
PRINT @SQL;
--EXECUTE (@SQL);
Itu akan memilih setiap baris pada setiap halaman apa pun TABLESAMPLE
klausa mengatakan (bahkan nol persen).