Indici doppi: un sovraccarico inutile
Possiamo facilmente immaginare l’inefficienza introdotta dalla doppia indicizzazione (ugualmente ordinata) di un attributo: SQL Server dovrà mantenere aggiornati due indici, organizzati in strutture B-Tree letteralmente identiche, senza trarre alcun beneficio da una delle due.
La stored procedure dbo.usp_drop_double_more_index, definita di seguito, permette di individuare ed eliminare i casi di doppia (tripla, ecc…) indicizzazione dei medesimi attributi.
use [AdventureWorks] go
if object_id(‘usp_drop_double_more_index’) is not null drop procedure dbo.usp_drop_double_more_index go
create procedure dbo.usp_drop_double_more_index ( @mode tinyint, @object_name varchar(4000), @table_type varchar(10), @index_name_prefix_order varchar(8), @debug tinyint ) as begin /* Descrizione: Individua ed elimina i casi di doppia (tripla, ecc..) indicizzazione sui medesimi attributi
Parametri: - @mode Modalità di utilizzo Può assumere i valori: 1= Exec Mode, 0= Print Mode. In exec-mode esegue la cancellazione degli indici doppi, in print-mode stampa i comandi T-SQL
- @object_name Nome di una tabella o lista di nomi di tabelle nel formato <schema_name>.<table_name> separati da “,” per le quali di desidera verificare ed eventualmente cancellare gli indici doppi (esempio <schema_name_1>.<table_name_1>, <schema_name_2>.<table_name_2>, …,
<schema_name_N>.<table_name_N>
- @table_type Tipo di tabella Può assumere: “VIEW” o “BASE TABLE”
- @index_name_prefix_order Determina, attraverso ordinamento, quale indice mantenere tra quelli doppi. Qualora si desideri mantenere un particolare indice o una particolare famiglia di indici il cui nome inizia ad esempio con “IX_”, sarà sufficiente specificare il prefisso desiderato in questo parametro
- @debug Modalità di debug Può assumere i valori: 1= Debug ON, 0= Debug OFF */
declare @msg varchar(256), @cmd varchar(max), @index_name_prefix_len int
– Controllo parametri di input – @mode if (@mode not in (0, 1)) begin set @msg = object_name(@@procid) + ’ : Modalità di esecuzione non supportata, ’ + ‘@mode può assumere i seguenti valori: ’ + ‘1= Modalità Exec, 0= Modalità Print. ’ + ‘Il valore di @mode passato è: ’ + ltrim(str(@mode)) raiserror(@msg, 16, 1) return end
– @debug if (@debug not in (0, 1)) begin set @msg = object_name(@@procid) + ’ : Modalità di debug non supportata, ’ + ‘@debug può assumere i seguenti valori: ’ + ‘1= Debug ON, 0= Debug OFF. ’ + ‘Il valore di @debug passato è: ’ + ltrim(str(@debug)) raiserror(@msg, 16, 1) return end
set nocount on
– Setup tabelle – tmphelpindex if object_id(’tmphelpindex’) is not null drop table tmphelpindex
create table tmphelpindex (id int identity(1, 1) not null, index_name sysname, index_description varchar(210), index_keys varchar(4000))
– tmpindexes if object_id(’tmpindexes’) is not null drop table tmpindexes
create table tmpindexes (id int identity(1, 1) not null, table_schema varchar(128), table_name sysname, index_name sysname, index_description varchar(210), index_keys varchar(4000))
– tmpindexes1 if object_id(’tmpindexes1’) is not null drop table tmpindexes1
create table tmpindexes1 (id int identity(1, 1) not null, table_schema varchar(128), table_name sysname, index_name sysname, index_description varchar(210), index_keys varchar(4000))
set @cmd = ’’ set @index_name_prefix_len = len(ltrim(rtrim(@index_name_prefix_order)))
if (@debug = 1) begin print (’@index_name_prefix_order= “’ + ltrim(rtrim(@index_name_prefix_order)) + ‘”’) print (’@index_name_prefix_len= ’ + ltrim(str(@index_name_prefix_len))) end
select @cmd = @cmd + ‘insert into tmphelpindex ’ + ’exec sp_helpindex ’’’ + i.table_schema + ‘.’ + i.table_name + ‘’’; ’ +
‘delete ’ + ’t ’ + ‘from ’ + ’tmphelpindex t ’ + ‘join ’ +
‘(select ’ + ‘min(t1.id) as id ’ + ‘from ’ + ’tmphelpindex t1 ’ + ‘where ’ + ’exists (select ’ + ‘id ’ + ‘from ’ + ’tmphelpindex t2 ’ + ‘where ’ + ‘(t1.index_keys=t2.index_keys) and ’ + ‘(t2.index_description like ‘‘clustered%’’)) and ’ + ‘(t1.index_description like ‘’nonclustered%’’)) tx on tx.id=t.id; ’ +
‘insert into tmpindexes ’ + ‘(table_schema, ’ + ’table_name, ’ + ‘index_name, ’ + ‘index_description, ’ + ‘index_keys) ’ + ‘select ’ + ’’’’ + i.table_schema + ‘’’,’ + ’’’’ + i.table_name + ‘’’, ’ + ‘index_name, ’ + ‘index_description, ’ + ‘index_keys ’ + ‘from ’ + ’tmphelpindex; ’ +
‘delete from tmphelpindex; ’ from information_schema.tables as i where ((charindex((’,’ + i.table_schema + ‘.’ + i.table_name + ‘,’), (’,’ + replace(@object_name, ’ ‘, ‘’) + ‘,’)) > 0) OR (@object_name=’’)) and (i.table_type=@table_type) order by i.table_schema, i.table_name
if (@debug = 1) print(@cmd) exec(@cmd)
if (ltrim(rtrim(@index_name_prefix_order)) <> ‘’) begin insert into tmpindexes1 (index_name, index_description, index_keys, table_schema, table_name) select index_name, index_description, index_keys, table_schema, table_name from tmpindexes where (left(index_name, @index_name_prefix_len) <> @index_name_prefix_order)
insert into tmpindexes1 (index_name, index_description, index_keys, table_schema, table_name) select index_name, index_description, index_keys, table_schema, table_name from tmpindexes where (left(index_name, @index_name_prefix_len) = @index_name_prefix_order) end else insert into tmpindexes1 (index_name, index_description, index_keys, table_schema, table_name) select index_name, index_description, index_keys, table_schema, table_name from tmpindexes
– Select double index on the same columns set @cmd = '’
select @cmd = @cmd + ‘drop index [’ + t.table_schema + ‘].[’ + t.table_name + ‘].[’ + t.index_name + ‘]; ’ + char(13) + char(10) from tmpindexes1 t join (select max(id) as max_id, count(index_keys) as index_count, table_schema, table_name, index_keys, index_description from tmpindexes1 group by table_schema, table_name, index_keys, index_description having (count(index_keys) > 1)
) t1 on (t.table_schema = t1.table_schema) and (t.table_name = t1.table_name) and (t.index_keys = t1.index_keys) and (t.index_description = t1.index_description) and (t.id < t1.max_id) order by t.index_name
if (@cmd = ‘’) begin print (’’) print (’+-+-+-+-+-+-+-+-+-+-+-+-+’) print (object_name(@@procid) + ‘: No double or more index found!’) end else begin print (’’) print (’+-+-+-+-+-+-+-+-+-+-+-+-+’) if (@mode = 0) print (object_name(@@procid) + ‘: Scrip drop index…’) else print (object_name(@@procid) + ‘: Start drop index…’) print (’’)
if (@mode = 0) print (@cmd) else begin exec (@cmd)
if (@debug = 1) print (@cmd)
print (’’) if (@@error = 0) print (‘Commands: ’ + char(13) + char(10) + @cmd + ’ completed successfully.’) else print (‘Commands: ’ + char(13) + char(10) + @cmd + ’ ended with errors.’) print (’’) end end
print (’’) print (object_name(@@procid) + ‘: …Execution end.’) print (’+-+-+-+-+-+-+-+-+-+-+-+-+’)
– Pulizia tabelle – tmphelpindex if object_id(’tmphelpindex’) is not null drop table tmphelpindex
– tmpindexes if object_id(’tmpindexes’) is not null drop table tmpindexes
– tmpindexes1 if object_id(’tmpindexes1’) is not null drop table tmpindexes1
set nocount off end
Esempio:
Ipotizziamo la situazione illustrata nella figura seguente per la tabella Person.Contact del database di esempio AdventureWorks.
L’attributo FirstName è caratterizzato da una tripla (identica) indicizzazione, riproducibile con i seguenti comandi CREATE INDEX:
create nonclustered index [IX__Person_Contact_FirstName] on [Person].[Contact](FirstName); go
create nonclustered index [IX__Person_Contact_FirstName_1] on [Person].[Contact](FirstName); go
create nonclustered index [IX__Person_Contact_FirstName_2] on [Person].[Contact](FirstName)
Cosa ci aspettiamo dalla stored procedure dbo.usp_drop_double_more_index ?
Ci aspettiamo che rilevi la tripla indicizzazione della colonna FirstName nella tabella Person.Contact.
Eseguiamo la stored procedure in modalità Print (@mode = 0):
EXEC dbo.usp_drop_double_more_index @mode=0, @object_name=‘Person.Contact’, @table_type=‘base table’, @index_name_prefix_order = ‘’, @debug=1
L’output ottenuto propone la cancellazione di due, dei tre indici definiti per l’attributo FieldName della tabella Person.Contact:
+-+-+-+-+-+-+-+-+-+-+-+-+ usp_drop_double_more_index: Scrip drop index…
drop index [Person].[Contact].[IX__Person_Contact_FirstName]; drop index [Person].[Contact].[IX__Person_Contact_FirstName_1];
usp_drop_double_more_index: …Execution end. +-+-+-+-+-+-+-+-+-+-+-+-+
Il comando precedente invocato in modalità Exec (@mode = 1) eseguirà la cancellazione fisica dei due indici:
EXEC dbo.usp_drop_double_more_index @mode=1, @object_name=’’, @table_type=‘base table’, @index_name_prefix_order = ‘’, @debug=1
Risultato:
+-+-+-+-+-+-+-+-+-+-+-+-+ usp_drop_double_more_index: Start drop index…
drop index [Person].[Contact].[IX__Person_Contact_FirstName]; drop index [Person].[Contact].[IX__Person_Contact_FirstName_1];
Commands: drop index [Person].[Contact].[IX__Person_Contact_FirstName]; drop index [Person].[Contact].[IX__Person_Contact_FirstName_1]; completed successfully.
usp_drop_double_more_index: …Execution end. +-+-+-+-+-+-+-+-+-+-+-+-+
Un’altra soluzione ottimizzata per SS2005 e SS2008 la potete trovare qui: Verificare la presenza di indici identici / duplicati