Share via


sys.dm_db_index_physical_stats (Transact-SQL)

Vengono restituite le informazioni sulle dimensioni e sulla frammentazione per i dati e gli indici della tabella o della vista specificata. Per un indice, viene restituita una riga per ogni livello dell'albero B in ogni partizione. Per un heap, viene restituita una riga per l'unità di allocazione IN_ROW_DATA di ogni partizione. Per i dati LOB (Large Object), viene restituita una riga per l'unità di allocazione LOB_DATA di ogni partizione. Se nella tabella esistono dati di overflow della riga, viene restituita una riga per l'unità di allocazione ROW_OVERFLOW_DATA in ogni partizione. Per informazioni sulle unità di allocazione e sulle partizioni, vedere Architettura delle strutture di dati di indici e tabelle.

Icona di collegamento a un argomentoConvenzioni della sintassi Transact-SQL

Sintassi

sys.dm_db_index_physical_stats ( 
    { database_id | NULL | 0 | DEFAULT }
  , { object_id | NULL | 0 | DEFAULT }
  , { index_id | NULL | 0 | -1 | DEFAULT }
  , { partition_number | NULL | 0 | DEFAULT }
  , { mode | NULL | DEFAULT }
)

Argomenti

  • database_id | NULL | 0 | DEFAULT
    ID del database. database_id è di tipo smallint. Gli input validi sono il numero di ID di un database, NULL, 0 o DEFAULT. Il valore predefinito è 0. NULL, 0 e DEFAULT sono valori equivalenti in questo contesto.

    Specificare NULL per restituire informazioni per tutti i database presenti nell'istanza di SQL Server. Se si specifica NULL per database_id, è necessario specificare NULL anche per object_id, index_id e partition_number.

    È possibile specificare la funzione predefinita DB_ID. Quando si utilizza DB_ID senza specificare un nome di database, il livello di compatibilità del database corrente deve essere 90 o un valore superiore.

  • object_id | NULL | 0 | DEFAULT
    ID oggetto della tabella o della vista in cui è contenuto l'indice. object_id è di tipo int.

    Gli input validi sono il numero di ID di una tabella o vista, NULL, 0 o DEFAULT. Il valore predefinito è 0. NULL, 0 e DEFAULT sono valori equivalenti in questo contesto.

    Specificare NULL per restituire le informazioni per tutte le tabelle e le viste nel database specificato. Se si specifica NULL per object_id, è necessario specificare NULL anche per index_id e partition_number.

  • index_id | 0 | NULL | -1 | DEFAULT
    ID dell'indice. index_id è di tipo int. Gli input validi sono il numero di ID di un indice, 0 se object_id è un heap, NULL, -1 o DEFAULT. Il valore predefinito è -1. NULL, -1 e DEFAULT sono valori equivalenti in questo contesto.

    Specificare NULL per restituire le informazioni per tutti gli indici per una vista o tabella di base. Se si specifica NULL per index_id, è necessario specificare NULL anche per partition_number.

  • partition_number | NULL | 0 | DEFAULT
    Numero di partizione nell'oggetto. partition_number è di tipo int. Gli input validi sono il valore di partion_number di un indice o un heap, NULL, 0 o DEFAULT. Il valore predefinito è 0. NULL, 0 e DEFAULT sono valori equivalenti in questo contesto.

    Specificare NULL per restituire le informazioni per tutte le partizioni dell'oggetto.

    partition_number è in base 1. Per gli indici o gli heap non partizionati partition_number è impostato su 1.

  • mode | NULL | DEFAULT
    Nome della modalità. mode specifica il livello di analisi utilizzato per ottenere le statistiche. mode è di tipo sysname. Gli input validi sono DEFAULT, NULL, LIMITED, SAMPLED o DETAILED. Il valore predefinito (NULL) è LIMITED.

Tabella restituita

Nome colonna

Tipo di dati

Descrizione

database_id

smallint

ID database della tabella o della vista.

object_id

int

ID oggetto della tabella o della vista in cui è contenuto l'indice.

index_id

int

ID indice di un indice.

0 = Heap.

partition_number

int

Numero di partizione in base 1 all'interno dell'oggetto proprietario; una tabella, una vista o un indice.

1 = Indice o heap non partizionato.

index_type_desc

nvarchar(60)

Descrizione del tipo di indice:

HEAP

CLUSTERED INDEX

NONCLUSTERED INDEX

PRIMARY XML INDEX

SPATIAL INDEX

XML INDEX

alloc_unit_type_desc

nvarchar(60)

Descrizione del tipo di unità di allocazione:

IN_ROW_DATA

LOB_DATA

ROW_OVERFLOW_DATA

L'unità di allocazione LOB_DATA contiene i dati archiviati nelle colonne di tipo text, ntext, image, varchar(max), nvarchar(max), varbinary(max) e xml. Per ulteriori informazioni, vedere Tipi di dati (Transact-SQL).

Nell'unità di allocazione ROW_OVERFLOW_DATA sono contenuti i dati archiviati nelle colonne di tipo varchar(n), nvarchar(n), varbinary(n) e sql_variant che sono stati spostati all'esterno delle righe. Per ulteriori informazioni, vedere Dati di overflow della riga che superano 8 KB.

index_depth

tinyint

Numero di livelli dell'indice.

1 = Heap o unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA.

index_level

tinyint

Livello corrente dell'indice.

0 per i livelli foglia dell'indice, gli heap e le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA.

Maggiore di 0 per i livelli di indice non foglia. index_level è il più elevato al livello di radice di un indice.

I livelli non foglia degli indici vengono elaborati solo quando mode = DETAILED.

avg_fragmentation_in_percent

float

Frammentazione logica per gli indici o frammentazione extent per gli heap nell'unità di allocazione IN_ROW_DATA.

Il valore viene misurato come percentuale e a tal fine vengono presi in considerazione più file. Per le definizioni di frammentazione logica ed extent, vedere la sezione Osservazioni.

0 per le unità di allocazione LOB_DATA e ROW_OVERFLOW_DATA.

NULL per gli heap quando mode = SAMPLED.

fragment_count

bigint

Numero di frammenti nel livello foglia di un'unità di allocazione IN_ROW_DATA. Per ulteriori informazioni sui frammenti, vedere la sezione Osservazioni.

NULL per i livelli non foglia di un indice e per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA.

NULL per gli heap quando mode = SAMPLED.

avg_fragment_size_in_pages

float

Numero medio di pagine in un frammento nel livello foglia di un'unità di allocazione IN_ROW_DATA.

NULL per i livelli non foglia di un indice e per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA.

NULL per gli heap quando mode = SAMPLED.

page_count

bigint

Numero totale di pagine di dati o di indice.

Per un indice, il numero totale di pagine di indice nel livello corrente dell'albero b-tree nell'unità di allocazione IN_ROW_DATA.

Per un heap, il numero totale di pagine di dati nell'unità di allocazione IN_ROW_DATA.

Per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA, il numero totale di pagine nell'unità di allocazione.

avg_page_space_used_in_percent

float

Percentuale media dello spazio di archiviazione dei dati utilizzato in tutte le pagine.

Per un indice, la media si applica al livello corrente dell'albero b-tree nell'unità di allocazione IN_ROW_DATA.

Per un heap, indica la media di tutte le pagine di dati nell'unità di allocazione IN_ROW_DATA.

Per le unità di allocazione LOB_DATA o ROW_OVERFLOW DATA, indica la media di tutte le pagine nell'unità di allocazione.

NULL quando mode = LIMITED.

record_count

bigint

Numero totale di record.

Per un indice, il numero totale di record si applica al livello corrente dell'albero b-tree nell'unità di allocazione IN_ROW_DATA.

Per un heap, indica il numero totale di pagine di dati nell'unità di allocazione IN_ROW_DATA.

NotaNota
Per un heap, il numero di record restituito da questa funzione potrebbe non corrispondere al numero di righe restituito eseguendo un SELECT COUNT (*) sull'heap. Questo perché una riga potrebbe contenere più record. Ad esempio, in alcune situazioni di aggiornamento, un'unica riga dell'heap potrebbe presentare un record di inoltro e un record inoltrato a seguito dell'operazione di aggiornamento. Inoltre, nell'archiviazione LOB_DATA la maggior parte delle righe LOB viene suddivisa in più record.

Per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA, indica il numero totale di record nell'unità di allocazione completa.

NULL quando mode = LIMITED.

ghost_record_count

bigint

Numero di record fantasma pronti per la rimozione tramite l'attività di pulizia dei record fantasma nell'unità di allocazione.

0 per i livelli non foglia di un indice nell'unità di allocazione IN_ROW_DATA.

NULL quando mode = LIMITED.

version_ghost_record_count

bigint

Numero di record fantasma mantenuti da una transazione di isolamento dello snapshot in attesa in un'unità di allocazione.

0 per i livelli non foglia di un indice nell'unità di allocazione IN_ROW_DATA.

NULL quando mode = LIMITED.

min_record_size_in_bytes

int

Dimensioni minime dei record in byte.

Per un indice, le dimensioni minime dei record si applicano al livello corrente dell'albero b-tree nell'unità di allocazione IN_ROW_DATA.

Per un heap, indica le dimensioni minime dei record nell'unità di allocazione IN_ROW_DATA.

Per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA, indica le dimensioni minime dei record nell'unità di allocazione completa.

NULL quando mode = LIMITED.

max_record_size_in_bytes

int

Dimensioni massime dei record in byte.

Per un indice, le dimensioni massime dei record si applicano al livello corrente dell'albero b-tree nell'unità di allocazione IN_ROW_DATA.

Per un heap, indica le dimensioni massime dei record nell'unità di allocazione IN_ROW_DATA.

Per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA, indica le dimensioni massime dei record nell'unità di allocazione completa.

NULL quando mode = LIMITED.

avg_record_size_in_bytes

float

Dimensioni medie dei record in byte.

Per un indice, le dimensioni medie dei record si applicano al livello corrente dell'albero b-tree nell'unità di allocazione IN_ROW_DATA.

Per un heap, indica le dimensioni medie dei record nell'unità di allocazione IN_ROW_DATA.

Per le unità di allocazione LOB_DATA o ROW_OVERFLOW_DATA, indica le dimensioni medie dei record nell'unità di allocazione completa.

NULL quando mode = LIMITED.

forwarded_record_count

bigint

Numero di record in un heap tramite i quali sono stati inoltrati puntatori a un altro percorso dei dati. Questo stato si verifica durante un aggiornamento, nel caso in cui non vi sia spazio sufficiente per archiviare la riga nel percorso originale.

NULL per qualsiasi unità di allocazione escluse le unità di allocazione IN_ROW_DATA per un heap.

NULL per gli heap quando mode = LIMITED.

compressed_page_count

bigint

Numero di pagine compresse.

  • Per gli heap, alle pagine appena allocate non viene applicata la compressione di tipo PAGE. A un heap viene applicata la compressione di tipo PAGE in due condizioni speciali, ovvero quando i dati vengono importati mediante un'operazione bulk o quando un heap viene ricompilato. Alle operazioni DML tipiche che determinano le allocazioni delle pagine non verrà applicata la compressione di tipo PAGE. Ricompilare un heap quando il valore compressed_page_count aumenta oltre la soglia desiderata.

  • Per tabelle in cui è presente un indice cluster, il valore compressed_page_count indica l'efficacia della compressione di tipo PAGE.

Osservazioni

La funzione a gestione dinamica sys.dm_db_index_physical_stats sostituisce l'istruzione DBCC SHOWCONTIG. Questa funzione a gestione dinamica non accetta parametri correlati da CROSS APPLY e OUTER APPLY.

Modalità di analisi

La modalità di esecuzione della funzione determina il livello di analisi eseguito per ottenere i dati statistici utilizzati dalla funzione. mode è specificato come LIMITED, SAMPLED o DETAILED. La funzione consente di attraversare le catene di pagine per le unità di allocazione che costituiscono le partizioni specificate della tabella o dell'indice. Per l'oggetto sys.dm_db_index_physical_stats viene richiesto solo un blocco a livello di tabella preventivo condiviso (IS), indipendentemente dalla modalità in cui viene eseguito. Per ulteriori informazioni sui blocchi, vedere Modalità blocco.

La modalità LIMITED è la più veloce ed esegue l'analisi del minor numero di pagine. Per un indice, viene eseguita l'analisi delle sole pagine di livello padre dell'albero b-tree (ovvero le pagine sopra il livello foglia). Per un heap, vengono esaminate solo le pagine associate PFS e IAM, mentre le pagine di dati non vengono analizzate. In SQL Server 2005, per tutte le pagine di un heap viene eseguita l'analisi in modalità LIMITED.

Con la modalità LIMITED, compressed_page_count è NULL perché il Motore di database consente di analizzare solo le pagine non foglia dell'albero b-tree e le pagine IAM e PFS dell'heap. Utilizzare la modalità SAMPLED per ottenere un valore stimato di compressed_page_count e la modalità DETAILED per ottenere il valore effettivo di compressed_page_count. Tramite la modalità SAMPLED vengono restituite le statistiche in base a un campione dell'1% di tutte le pagine nell'indice o nell'heap. I risultati della modalità SAMPLED devono essere considerati approssimativi. Se nell'indice o nell'heap sono presenti meno di 10.000 pagine, viene utilizzata la modalità DETAILED invece della modalità SAMPLED.

La modalità DETAILED esegue l'analisi di tutte le pagine e restituisce tutte le statistiche.

La velocità della modalità è decrescente da LIMITED a DETAILED, a causa dell'aumento della quantità di lavoro. Per misurare rapidamente le dimensioni o il livello di frammentazione di una tabella o di un indice, utilizzare la modalità LIMITED. Si tratta della modalità più veloce e non restituisce una riga per ogni livello non foglia nell'unità di allocazione IN_ROW_DATA dell'indice.

Utilizzo di funzioni di sistema per specificare i valori dei parametri

È possibile utilizzare le funzioni DB_ID e OBJECT_ID di Transact-SQL per specificare un valore per i parametri database_id e object_id. Se si passano valori non validi a queste funzioni, tuttavia, si potrebbero provocare risultati imprevisti. Se, ad esempio, non è possibile trovare il nome del database o dell'oggetto perché inesistente o perché l'ortografia non è corretta, verrà restituito NULL da entrambe le funzioni. Il valore NULL viene interpretato dalla funzione sys.dm_db_index_physical_stats come valore di carattere jolly che specifica tutti i database o tutti gli oggetti.

La funzione OBJECT_ID viene inoltre elaborata prima della chiamata della funzione sys.dm_db_index_physical_stats e pertanto viene valutata nel contesto del database corrente, anziché del database specificato in database_id. Questo comportamento può causare la restituzione di un valore NULL da parte della funzione OBJECT_ID. In alternativa, se il nome dell'oggetto esiste sia nel contesto del database corrente, sia nel database specificato, può essere restituito un messaggio di errore. Negli esempi seguenti vengono illustrati questi risultati imprevisti.

Nell'esempio seguente OBJECT_ID viene valutato nel contesto del database master. Poiché Person.Address non esiste in master, la funzione restituisce NULL. Se viene specificato NULL come object_id, vengono restituiti tutti gli oggetti del database. Gli stessi risultati vengono restituiti quando viene specificato un oggetto non valido.

USE master;
GO
SELECT * FROM sys.dm_db_index_physical_stats
    (DB_ID(N'AdventureWorks'), OBJECT_ID(N'Person.Address'), NULL, NULL , 'DETAILED');
GO

Nell'esempio seguente vengono illustrati i risultati della specifica di un nome di oggetto valido che esiste sia nel contesto del database corrente che in quello del database specificato nel parametro database_id della funzione sys.dm_db_index_physical_stats. Viene restituito un errore perché il valore ID restituito da OBJECT_ID non corrisponde al valore ID dell'oggetto nel database specificato.

CREATE DATABASE Test;
GO
USE Test;
GO
CREATE SCHEMA Person;
GO
CREATE Table Person.Address(c1 int);
GO
USE AdventureWorks2008R2;
GO
SELECT * FROM sys.dm_db_index_physical_stats
    (DB_ID(N'Test'), OBJECT_ID(N'Person.Address'), NULL, NULL , 'DETAILED');
GO
-- Clean up temporary database.
DROP DATABASE Test;
GO

Procedura consigliata

Quando si utilizza DB_ID o OBJECT_ID, verificare sempre che venga restituito un ID valido. Ad esempio, quando si utilizza OBJECT_ID, specificare un nome in tre parti come OBJECT_ID(N'AdventureWorks2008R2.Person.Address') oppure testare il valore restituito dalle funzioni prima del relativo utilizzo nella funzione sys.dm_db_index_physical_stats. Negli esempi A e B seguenti viene illustrato come specificare ID database e oggetto in modo sicuro.

Rilevamento della frammentazione

La frammentazione si verifica nel corso delle modifiche dei dati (istruzioni INSERT, UPDATE e DELETE) apportate alla tabella e, di conseguenza, agli indici in essa definiti. Poiché tali modifiche in genere non sono distribuite in modo equo tra le righe della tabella e gli indici, il livello di riempimento di ogni pagina può variare nel tempo. Nel caso di query che eseguono l'analisi di una parte o di tutti gli indici di una tabella, questo tipo di frammentazione potrebbe comportare letture di pagine aggiuntive, operazione che ostacola l'analisi parallela dei dati.

L'algoritmo per il calcolo della frammentazione è più preciso in SQL Server 2008 rispetto a SQL Server 2000. Di conseguenza, verranno visualizzati valori di frammentazione superiori. Ad esempio, in SQL Server 2000, una tabella non è considerata frammentata se nello stesso extent sono presenti le pagine 11 e 13 ma non la pagina 12, tuttavia, poiché per accedere a queste due pagine sarebbero necessarie due operazioni fisiche di I/O, in SQL Server 2008 la tabella viene considerata frammentata.

Il livello di frammentazione di un indice o di un heap viene illustrato nella colonna avg_fragmentation_in_percent. Per gli heap, il valore rappresenta la frammentazione extent dell'heap. Per gli indici, il valore rappresenta la frammentazione logica dell'indice. Diversamente da DBCC SHOWCONTIG, gli algoritmi di calcolo della frammentazione considerano in entrambi i casi l'archivio che si estende su più file e sono pertanto accurati.

Frammentazione logica

Percentuale delle pagine non ordinate nelle pagine foglia di un indice. Una pagina risulta non ordinata quando la pagina fisica successiva allocata all'indice è diversa da quella a cui fa riferimento il puntatore di pagina successiva nella pagina foglia corrente.

Frammentazione extent

Percentuale di extent non ordinati nelle pagine foglia di un heap. Un extent è considerato non ordinato quando l'extent in cui è contenuta la pagina corrente per un heap non è fisicamente l'extent successivo a quello in cui è contenuta la pagina precedente.

Il valore per avg_fragmentation_in_percent deve essere il più possibile prossimo a zero per ottenere le prestazioni massime. Tuttavia, i valori compresi tra 0% e 10% possono essere accettabili. Per ridurre questi valori, è possibile utilizzare tutti i metodi per ridurre la frammentazione, ad esempio ricompilazione, riorganizzazione o ricreazione. Per ulteriori informazioni su come analizzare il livello di frammentazione in un indice, vedere Riorganizzazione e ricompilazione degli indici.

Riduzione della frammentazione in un indice

Quando la frammentazione di un indice avviene in modo tale da influire sulle prestazioni delle query, è possibile effettuare una delle tre operazioni seguenti:

  • Rimuovere e ricreare l'indice cluster.

    Ricreando un indice cluster i dati vengono ridistribuiti e si ottengono pagine di dati complete. È possibile configurare il livello di riempimento tramite l'opzione FILLFACTOR dell'istruzione CREATE INDEX. Gli svantaggi di questo metodo consistono nel fatto che l'indice è offline durante il ciclo di rimozione e ricreazione e che l'operazione è atomica. Se la creazione dell'indice viene interrotta, l'indice non viene ricreato. Per ulteriori informazioni, vedere CREATE INDEX (Transact-SQL).

  • Utilizzare ALTER INDEX REORGANIZE, l'alternativa a DBCC INDEXDEFRAG, per riordinare le pagine di livello foglia dell'indice in un ordine logico. Poiché si tratta di un'operazione online, l'indice è disponibile durante l'esecuzione dell'istruzione. L'operazione può essere interrotta senza perdere il lavoro già completato. Lo svantaggio di questo metodo è che il processo di riorganizzazione dei dati non è tanto efficiente quanto un'operazione di ricompilazione dell'indice e non aggiorna le statistiche.

  • Utilizzare ALTER INDEX REBUILD, l'alternativa a DBCC DBREINDEX, per ricompilare l'indice online o offline. Per ulteriori informazioni, vedere ALTER INDEX (Transact-SQL).

La sola frammentazione non è un motivo sufficiente per riorganizzare o ricompilare un indice. L'effetto principale della frammentazione è che rallenta la velocità read-ahead effettiva delle pagine durante le analisi di indici, provocando tempi di risposta più lenti. Se il carico di lavoro della query in una tabella o in un indice frammentato non comporta analisi, poiché il carico di lavoro è rappresentato principalmente da ricerche singleton, la rimozione della frammentazione potrebbe non avere alcun effetto. Per ulteriori informazioni, vedere questo sito Web Microsoft.

Nota

L'esecuzione di DBCC SHRINKFILE o DBCC SHRINKDATABASE potrebbe provocare la frammentazione se un indice viene spostato completamente o in parte durante l'operazione di compattazione. Pertanto, se è necessario eseguire un'operazione di compattazione, è consigliabile eseguirla prima della rimozione della frammentazione.

Riduzione della frammentazione in un heap

Per ridurre la frammentazione extent di un heap, creare un indice cluster nella tabella e quindi rimuovere l'indice. I dati vengono quindi ridistribuiti in maniera ottimale durante la creazione dell'indice cluster, tenendo in considerazione la distribuzione dello spazio disponibile nel database. Quando l'indice cluster viene successivamente rimosso per ricreare l'heap, i dati non vengono spostati e rimangono nella posizione ottimale. Per informazioni sull'esecuzione di queste operazioni, vedere CREATE INDEX e DROP INDEX.

Nota di attenzioneAttenzione

Se in una tabella viene creato ed eliminato un indice cluster, tutti gli indici non cluster in tale tabella vengono ricompilati due volte.

Compattazione dei dati LOB (Large Object)

Per impostazione predefinita, l'istruzione ALTER INDEX REORGANIZE consente di compattare le pagine contenenti dati LOB. Poiché le pagine LOB non vengono deallocate quando sono vuote, la compattazione di questi dati può migliorare l'utilizzo dello spazio su disco se sono stati cancellati molti dati LOB o se viene eliminata una colonna LOB.

La riorganizzazione di un indice cluster specificato compatta tutte le colonne LOB contenute nell'indice cluster. La riorganizzazione di un indice non cluster compatta tutte le colonne LOB che non siano colonne non chiave (incluse) nell'indice. Quando viene specificato ALL nell'istruzione, tutti gli indici associati alla tabella o vista vengono riorganizzati. Inoltre, tutte le colonne LOB associate all'indice cluster, alla tabella sottostante o all'indice non cluster con colonne vengono compattati.

Valutazione dell'utilizzo dello spazio su disco

Nella colonna avg_page_space_used_in_percent viene indicato il livello di riempimento pagina. Per ottenere un utilizzo ottimale dello spazio su disco, questo valore deve essere prossimo al 100% per un indice in cui non verranno inclusi molti inserimenti casuali. Tuttavia, un indice con molti inserimenti casuali e pagine con un alto livello di riempimento avrà un maggior numero di divisioni di pagina e di conseguenza una maggiore frammentazione. Pertanto, per ridurre il numero di divisioni di pagina, è necessario che il valore sia inferiore a 100%. La ricompilazione di un indice con l'opzione FILLFACTOR specificata consente la modifica del livello di riempimento pagina in modo che corrisponda al modello di query nell'indice. Per ulteriori informazioni sul fattore di riempimento, vedere Fattore di riempimento. Inoltre, ALTER INDEX REORGANIZE consentirà di compattare un indice cercando di riempire le pagine fino al fattore di riempimento FILLFACTOR specificato per ultimo. In questo modo, il valore in avg_space_used_in_percent aumenta. Si noti che tramite ALTER INDEX REORGANIZE non può essere ridotto il livello di riempimento pagina. È necessario invece eseguire una ricompilazione dell'indice.

Valutazione dei frammenti di indice

Un frammento è composto da pagine foglia fisicamente consecutive nello stesso file per un'unità di allocazione. Ogni indice ha almeno un frammento. Il numero massimo di frammenti che un indice può avere è uguale al numero di pagine nel livello foglia dell'indice. La presenza di frammenti più grandi indica che è necessaria una quantità minore di I/O su disco per leggere lo stesso numero di pagine. Pertanto, maggiore sarà il valore di avg_fragment_size_in_pages e migliori saranno le prestazioni di analisi dell'intervallo. I valori avg_fragment_size_in_pages e avg_fragmentation_in_percent sono inversamente proporzionali. La ricompilazione o la riorganizzazione di un indice dovrebbe quindi ridurre la quantità di frammentazione e far aumentare le dimensioni del frammento.

Autorizzazioni

Sono richieste le autorizzazioni seguenti:

  • Autorizzazione CONTROL per l'oggetto specificato all'interno del database.

  • Autorizzazione VIEW DATABASE STATE per la restituzione di informazioni su tutti gli oggetti all'interno del database specificato, tramite il carattere jolly di oggetto @object_id=NULL.

  • Autorizzazione VIEW SERVER STATE per la restituzione di informazioni su tutti i database, tramite il carattere jolly di database @database_id = NULL.

La concessione di VIEW DATABASE STATE consente la restituzione di tutti gli oggetti nel database, indipendentemente dalle eventuali autorizzazioni CONTROL negate per oggetti specifici.

La negazione di VIEW DATABASE STATE non consente la restituzione di tutti gli oggetti nel database, indipendentemente dalle eventuali autorizzazioni CONTROL concesse per oggetti specifici. Quando viene specificato il carattere jolly di database @database_id=NULL, inoltre, il database viene omesso.

Per ulteriori informazioni, vedere Funzioni e viste a gestione dinamica (Transact-SQL).

Esempi

A. Restituzione delle informazioni su una tabella specifica

Nell'esempio seguente vengono restituite le dimensioni e le statistiche di frammentazione per tutti gli indici e le partizione della tabella Person.Address nel database AdventureWorks2008R2. La modalità di analisi viene impostata su 'LIMITED' per ottimizzare le prestazioni e limitare le statistiche restituite. L'esecuzione di questa query richiede almeno l'autorizzazione CONTROL nella tabella Person.Address.

DECLARE @db_id SMALLINT;
DECLARE @object_id INT;

SET @db_id = DB_ID(N'AdventureWorks2008R2');
SET @object_id = OBJECT_ID(N'AdventureWorks2008R2.Person.Address');

IF @db_id IS NULL
BEGIN;
    PRINT N'Invalid database';
END;
ELSE IF @object_id IS NULL
BEGIN;
    PRINT N'Invalid object';
END;
ELSE
BEGIN;
    SELECT * FROM sys.dm_db_index_physical_stats(@db_id, @object_id, NULL, NULL , 'LIMITED');
END;
GO

B. Restituzione delle informazioni su un heap

Nell'esempio seguente vengono restituite le statistiche per l'heap dbo.DatabaseLog nel database AdventureWorks2008R2. Poiché la tabella contiene dati LOB, viene restituita una riga per l'unità di allocazione LOB_DATA in aggiunta alla riga restituita per IN_ROW_ALLOCATION_UNIT che archivia le pagine di dati dell'heap. L'esecuzione di questa query richiede almeno l'autorizzazione CONTROL per la tabella dbo.DatabaseLog.

DECLARE @db_id SMALLINT;
DECLARE @object_id INT;
SET @db_id = DB_ID(N'AdventureWorks2008R2');
SET @object_id = OBJECT_ID(N'AdventureWorks2008R2.dbo.DatabaseLog');
IF @object_id IS NULL 
BEGIN;
    PRINT N'Invalid object';
END;
ELSE
BEGIN;
    SELECT * FROM sys.dm_db_index_physical_stats(@db_id, @object_id, 0, NULL , 'DETAILED');
END;
GO

C. Restituzione delle informazioni su tutti i database

Nell'esempio seguente vengono restituite tutte le statistiche per tutte le tabelle e gli indici presenti nell'istanza di SQL Server specificando il carattere jolly NULL per tutti i parametri. L'esecuzione di questa query richiede l'autorizzazione VIEW SERVER STATE.

SELECT * FROM sys.dm_db_index_physical_stats (NULL, NULL, NULL, NULL, NULL);
GO

D. Utilizzo di sys.dm_db_index_physical_stats in uno script per ricompilare o riorganizzare gli indici

Nell'esempio seguente vengono riorganizzate o ricompilate automaticamente tutte le partizioni di un database che hanno una frammentazione media superiore al 10%. Per l'esecuzione di questa query, è necessario disporre dell'autorizzazione VIEW DATABASE STATE. Nell'esempio viene specificato DB_ID come primo parametro senza includere un nome di database. Se il database corrente ha un livello di compatibilità pari a 80 o inferiore, viene generato un errore. Per risolvere l'errore, sostituire DB_ID() con un nome di database valido. Per ulteriori informazioni sui livelli di compatibilità dei database, vedere sp_dbcmptlevel (Transact-SQL).

-- Ensure a USE <databasename> statement has been executed first.
SET NOCOUNT ON;
DECLARE @objectid int;
DECLARE @indexid int;
DECLARE @partitioncount bigint;
DECLARE @schemaname nvarchar(130); 
DECLARE @objectname nvarchar(130); 
DECLARE @indexname nvarchar(130); 
DECLARE @partitionnum bigint;
DECLARE @partitions bigint;
DECLARE @frag float;
DECLARE @command nvarchar(4000); 
-- Conditionally select tables and indexes from the sys.dm_db_index_physical_stats function 
-- and convert object and index IDs to names.
SELECT
    object_id AS objectid,
    index_id AS indexid,
    partition_number AS partitionnum,
    avg_fragmentation_in_percent AS frag
INTO #work_to_do
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED')
WHERE avg_fragmentation_in_percent > 10.0 AND index_id > 0;

-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR FOR SELECT * FROM #work_to_do;

-- Open the cursor.
OPEN partitions;

-- Loop through the partitions.
WHILE (1=1)
    BEGIN;
        FETCH NEXT
           FROM partitions
           INTO @objectid, @indexid, @partitionnum, @frag;
        IF @@FETCH_STATUS < 0 BREAK;
        SELECT @objectname = QUOTENAME(o.name), @schemaname = QUOTENAME(s.name)
        FROM sys.objects AS o
        JOIN sys.schemas as s ON s.schema_id = o.schema_id
        WHERE o.object_id = @objectid;
        SELECT @indexname = QUOTENAME(name)
        FROM sys.indexes
        WHERE  object_id = @objectid AND index_id = @indexid;
        SELECT @partitioncount = count (*)
        FROM sys.partitions
        WHERE object_id = @objectid AND index_id = @indexid;

-- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding.
        IF @frag < 30.0
            SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REORGANIZE';
        IF @frag >= 30.0
            SET @command = N'ALTER INDEX ' + @indexname + N' ON ' + @schemaname + N'.' + @objectname + N' REBUILD';
        IF @partitioncount > 1
            SET @command = @command + N' PARTITION=' + CAST(@partitionnum AS nvarchar(10));
        EXEC (@command);
        PRINT N'Executed: ' + @command;
    END;

-- Close and deallocate the cursor.
CLOSE partitions;
DEALLOCATE partitions;

-- Drop the temporary table.
DROP TABLE #work_to_do;
GO

E. Utilizzo di sys.dm_db_index_physical_stats per visualizzare il numero di pagine a cui è stata applicata la compressione di pagina

Nell'esempio seguente viene illustrato come visualizzare e confrontare il numero complessivo di pagine rispetto a quelle a cui è stata applicata una compressione di riga e di pagina. Tali informazioni possono essere utilizzate per determinare i vantaggi offerti dalla compressione per un indice o per una tabella.

SELECT o.name,
    ips.partition_number,
    ips.index_type_desc,
    ips.record_count, ips.avg_record_size_in_bytes,
    ips.min_record_size_in_bytes,
    ips.max_record_size_in_bytes,
    ips.page_count, ips.compressed_page_count
FROM sys.dm_db_index_physical_stats ( DB_ID(), NULL, NULL, NULL, 'DETAILED') ips
JOIN sys.objects o on o.object_id = ips.object_id
ORDER BY record_count DESC;

F. Utilizzo di sys.dm_db_index_physical_stats in modalità SAMPLED

Nell'esempio seguente viene illustrato il modo in cui tramite la modalità SAMPLED vengono restituiti risultati approssimativi diversi rispetto a quelli della modalità DETAILED.

CREATE TABLE t3 (col1 int PRIMARY KEY, col2 varchar(500)) WITH(DATA_COMPRESSION = PAGE);
GO
BEGIN TRAN
DECLARE @idx int = 0;
WHILE @idx < 1000000
BEGIN
    INSERT INTO t3 (col1, col2) 
    VALUES (@idx, 
    REPLICATE ('a', 100) + CAST (@idx as varchar(10)) + REPLICATE ('a', 380))
    SET @idx = @idx + 1
END
COMMIT;
GO
SELECT page_count, compressed_page_count, forwarded_record_count, * 
FROM sys.dm_db_index_physical_stats (db_id(), 
    object_id ('t3'), null, null, 'SAMPLED');
SELECT page_count, compressed_page_count, forwarded_record_count, * 
FROM sys.dm_db_index_physical_stats (db_id(), 
    object_id ('t3'), null, null, 'DETAILED');