Lý lịch
Dữ liệu cho đối tượng thống kê được thu thập bằng cách sử dụng câu lệnh có dạng:
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)
Bạn có thể thu thập tuyên bố này với Sự kiện mở rộng hoặc Profiler ( SP:StmtCompleted
).
Các truy vấn tạo thống kê thường truy cập vào bảng cơ sở (chứ không phải là một chỉ mục không được bao gồm) để tránh việc phân cụm các giá trị xảy ra tự nhiên trên các trang chỉ mục không được bao gồm.
Số lượng hàng được lấy mẫu tùy thuộc vào số lượng toàn bộ trang được chọn để lấy mẫu. Mỗi trang của bảng được chọn hoặc không. Tất cả các hàng trên các trang được chọn đóng góp vào số liệu thống kê.
Số ngẫu nhiên
SQL Server sử dụng trình tạo số ngẫu nhiên để quyết định xem một trang có đủ điều kiện hay không. Trình tạo được sử dụng trong trường hợp này là trình tạo số ngẫu nhiên Lehmer với các giá trị tham số như dưới đây:
X tiếp theo = X hạt giống * 7 5 mod (2 31 - 1)
Giá trị của được tính là tổng của:Xseed
Các nguyên thấp một phần của ( bigint
) cơ sở của bảng partition_id
ví dụ
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;
Giá trị được chỉ định trong REPEATABLE
mệnh đề
- Đối với mẫu
UPDATE STATISTICS
, REPEATABLE
giá trị là 1.
- Giá trị này được hiển thị trong
m_randomSeed
phần tử của thông tin gỡ lỗi nội bộ của phương thức truy cập được hiển thị trong kế hoạch thực hiện khi cờ theo dõi 8666 được bật, ví dụ:<Field FieldName="m_randomSeed" FieldValue="1" />
Đối với SQL Server 2012, tính toán này xảy ra trong sqlmin!UnOrderPageScanner::StartScan
:
mov edx,dword ptr [rcx+30h]
add edx,dword ptr [rcx+2Ch]
trong đó bộ nhớ tại [rcx+30h]
chứa 32 bit thấp của id phân vùng và bộ nhớ tại [rcx+2Ch]
chứa REPEATABLE
giá trị được sử dụng.
Trình tạo số ngẫu nhiên được khởi tạo sau trong cùng một phương thức, gọi sqlmin!RandomNumGenerator::Init
, trong đó hướng dẫn:
imul r9d,r9d,41A7h
... nhân hạt giống với 41A7
hex (16807 thập phân = 7 5 ) như thể hiện trong phương trình trên.
Các số ngẫu nhiên sau này (đối với các trang riêng lẻ) được tạo bằng cách sử dụng cùng một mã cơ bản được nhập vào sqlmin!UnOrderPageScanner::SetupSubScanner
.
Statman
Đối với StatMan
truy vấn mẫu được hiển thị ở trên, các trang tương tự sẽ được thu thập như đối với câu lệnh 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
Điều này sẽ phù hợp với đầu ra của:
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';
Vỏ cạnh
Một hậu quả của việc sử dụng trình tạo số ngẫu nhiên MINSTD Lehmer là không nên sử dụng giá trị seed bằng 0 và int.max vì điều này sẽ dẫn đến thuật toán tạo ra một chuỗi các số 0 (chọn mỗi trang).
Mã phát hiện 0 và sử dụng một giá trị từ 'đồng hồ' của hệ thống làm hạt giống trong trường hợp đó. Nó không làm như vậy nếu hạt giống là int.max ( 0x7FFFFFFF
= 2 31 - 1).
Chúng ta có thể thiết kế kịch bản này vì hạt giống ban đầu được tính bằng tổng 32 bit thấp của id phân vùng và REPEATABLE
giá trị. Các REPEATABLE
giá trị đó sẽ dẫn đến việc hạt giống là int.max và do đó tất cả các trang được chọn để mẫu là:
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;
Làm việc đó thành một ví dụ hoàn chỉnh:
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);
Điều đó sẽ chọn mọi hàng trên mỗi trang bất kể TABLESAMPLE
mệnh đề nói gì (thậm chí là không phần trăm).