If you’ve been a DBA for a while, you likely have
encountered developers who are extremely proficient
in front-end application design but have a rather
limited understanding of the database technology
they’re using as the back end. In these situations, it
seems that indexes are only applied as an afterthought,
if they are even applied at all.
Once these applications start to process a serious
amount of data, performance problems begin to surface.
This typically happens a few days after the application
is released to production. A DBA at this point might be
asked to take a look and see what can be done.
After some preliminary investigations and performing
a few traces, the DBA might decide to perform
a full index analysis. The Database Engine Tuning
Advisor might be useful here, but I prefer getting to
know my indexes personally rather than relying on a
wizard’s best educated guess. So, I
wrote two T-SQL stored procedures
to aid in this task: sp_GenerateIndexes
and sp_ListAllIndexes, which
work on SQL Server 2005 and SQL Server 2000.
The sp_GenerateIndexes stored procedure generates
SQL statements that can drop and create indexes for a
specified table. By having the SQL code drop and create
indexes, you can do some experimenting by adding or
removing columns as you see fit. If you’re unhappy
with the results, you can simply use sp_GenerateIndexes
again to drop
the new indexes
and re-create the
original indexes
by running the
SQL code that was generated previously. You would, of
course, do this experimentation only in a test environment,
not on your live production server.
The sp_GenerateIndexes stored procedure also creates
an extremely useful report that details the various
properties of each index, such as whether the index is
clustered, the sort order, and the fill factor. Figure 1
contains an example of a report that’s been edited for
space. I tried to keep the column names short so that
I could include as much information as possible in the
report. Here is a brief description of what each column
in the report contains:
- TableName—Specifies the name of the table.
- IndexName—Specifies name of the index.
- ID—Specifies the index ID as retrieved from the
sysindex table. (Note that 0 and 255 aren’t present
because they have special meanings.)
- Clust—Specifies whether the index is clustered.
- Uniq—Specifies whether the index is unique.
- PK—Specifies whether the index is a primary key
constraint.
- IgDup—Specifies whether the index ignores duplicate
entries.
- Stat—Specifies whether the index is really an autogenerated
statistic and not a true index.
- Hyp—Specifies whether the index is really a hypothetical
index created by the Index Tuning Wizard
and not a true index.
- Fill—Notes the original fill factor that was specified
when the index was created.
- PadIdx—Specifies whether index padding is enabled
for the index.
- ColumnNames—Provides a concatenated list of all
the columns in the index.
As you can see from the sample report in Figure
1, the MyTable table has a clustered primary key, a
unique index, and a nonclustered compound index
with a descending sort order on its second column. The
original fill factor was specified as 100 for each index.
What stands out in this report is that the third index is a
compound index that includes the same column as the
clustered index (i.e., TheID). This is wasteful because
each index row in the nonclustered index already contains
a row-locater, which in this case, is the clustered
index key (i.e., TheID).
The second stored procedure that I created—
sp_ListAllIndexes—iterates through all the tables in
the current database, calling sp_GenerateIndexes for
each table found. The output from sp_GenerateIndexes
is captured and stored in a worktable, then displayed at
the end as one big master report.
After running sp_ListAllIndexes, you’ll have a
master report that lists all the indexes on all the tables
in the current database. A quick glance at this report
is often all you need to make a few strategic decisions,
such as whether to add clustered indexes to heap tables
or whether to remove obviously useless indexes. If there
are no indexes to report on, you can bring the empty
report to the development team and politely explain to
them that a database without indexes is about as useful
as a mouse without buttons.
End of Article