sfondo
I dati per l'oggetto statistico vengono raccolti usando un'istruzione del modulo:
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)
È possibile raccogliere questa affermazione con Eventi estesi o Profiler ( SP:StmtCompleted
).
Le query di generazione delle statistiche spesso accedono alla tabella di base (anziché a un indice non cluster) per evitare il raggruppamento di valori che si verifica naturalmente nelle pagine dell'indice non cluster.
Il numero di righe campionate dipende dal numero di pagine intere selezionate per il campionamento. Ogni pagina della tabella è selezionata o non lo è. Tutte le righe delle pagine selezionate contribuiscono alle statistiche.
Numeri casuali
SQL Server utilizza un generatore di numeri casuali per decidere se una pagina è idonea o meno. Il generatore utilizzato in questo caso è il generatore di numeri casuali Lehmer con valori di parametro come mostrato di seguito:
X successivo = X seme * 7 5 mod (2 31 - 1)
Il valore di viene calcolato come la somma di:Xseed
La parte intera bassa della bigint
tabella di base ( ) partition_id
ad es
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;
Il valore specificato nella REPEATABLE
clausola
- Per il campione
UPDATE STATISTICS
, il REPEATABLE
valore è 1.
- Questo valore è esposto
m_randomSeed
nell'elemento delle informazioni di debug interne del metodo di accesso mostrate nei piani di esecuzione quando è abilitato il flag di traccia 8666, ad esempio<Field FieldName="m_randomSeed" FieldValue="1" />
Per SQL Server 2012, questo calcolo si verifica in sqlmin!UnOrderPageScanner::StartScan
:
mov edx,dword ptr [rcx+30h]
add edx,dword ptr [rcx+2Ch]
dove memory at [rcx+30h]
contiene i 32 bit bassi dell'ID partizione e memory at [rcx+2Ch]
contiene il REPEATABLE
valore in uso.
Il generatore di numeri casuali viene inizializzato successivamente nello stesso metodo, chiamando sqlmin!RandomNumGenerator::Init
, dove l'istruzione:
imul r9d,r9d,41A7h
... moltiplica il seme per 41A7
esadecimale (16807 decimale = 7 5 ) come mostrato nell'equazione sopra.
I numeri casuali successivi (per le singole pagine) vengono generati utilizzando lo stesso codice di base incorporato sqlmin!UnOrderPageScanner::SetupSubScanner
.
Statman
Per la StatMan
query di esempio mostrata sopra, verranno raccolte le stesse pagine dell'istruzione 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
Questo corrisponderà all'output di:
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';
Custodia per bordi
Una conseguenza dell'utilizzo del generatore di numeri casuali MINSTD Lehmer è che i valori di seed zero e int.max non devono essere utilizzati in quanto ciò comporterebbe l'algoritmo che produce una sequenza di zero (selezionando ogni pagina).
Il codice rileva zero e utilizza un valore dall'orologio di sistema come seme in quel caso. Non fa lo stesso se il seme è int.max ( 0x7FFFFFFF
= 2 31 - 1).
Possiamo progettare questo scenario poiché il seed iniziale viene calcolato come la somma dei 32 bit bassi dell'ID partizione e del REPEATABLE
valore. Il REPEATABLE
valore che determinerà che il seme sia int.max e quindi ogni pagina selezionata per il campione è:
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;
Elaborandolo in un esempio completo:
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);
Che selezionerà ogni riga su ogni pagina qualunque sia la TABLESAMPLE
clausola dice (anche zero per cento).