DEFINITIONS

Definitions More Info.
Definition ID137
TitleSQL
CategoryMS_SCRIPT
DefinitionClusteredIndexAnalyser
Definition Description/*
================================================================================

NAME : ClusteredIndexAnalyser
DESCRIPTION : Identifies each user Table's Clustered index along with the columns that
make up that Clustered index. In addition, the column types, their max
size in bytes, total index size in bytes, Table Row Count and Total
Clustered Index size in kilobytes is calculated to help determine the
largest, or most inefficient Clustered indexes. Additionaly whether the
Clustered index is unique, the number of non clustered indexes on the table,
and another calculation of Potential cost of the CLUSTERED INDEX with the the NONCLUSTERED INDEXES
(ClusteredIndexSizeKB * Non Clustered Indexes) since all NONCLUSTERED INDEXES contain the CLUSTERED INDEX.

CREATED BY : CGW
DATE : 20/01/2010
MODIFIED : CAJ 12/03/2010

================================================================================
*/
SET NOCOUNT ON

------------------------------------------------------------------------
-- Declare Variables
------------------------------------------------------------------------
DECLARE @Iteration INT; -- Variable used as an iterator
DECLARE @AddColumnIteration INT; -- An additional Iterator to determine columns of an index
DECLARE @Tmp NVARCHAR(MAX); -- Variable used in several areas to temporarily have an SQL statement which can be executed
DECLARE @ClusterKeyName SYSNAME; -- The Clustered Key Name (used within the Cursor)
DECLARE @SchemaName SYSNAME; -- The Schema Name (used within the Cursor) ** 12/03/2010 CAJ **
DECLARE @TableName SYSNAME; -- The Table Name (used within the Cursor)
DECLARE @ClusteredIndexID INT; -- ClusteredIndexID from the ClusteredIndexes TEMP TABLE
DECLARE @ClusteredIndexType INT; -- Declaring a constant
DECLARE @NonClusteredIndexType INT; -- Declaring a constant

SET @ClusteredIndexType = 1 -- Index Type 1 is a CLUSTERED INDEX
SET @NonClusteredIndexType = 2 -- Index Type 2 is a NONCLUSTERED INDEX

------------------------------------------------------------------------
-- Declare #Result Temp Table
------------------------------------------------------------------------
IF OBJECT_ID(N'tempdb..#Result', N'U') IS NOT NULL
DROP TABLE #Result;

CREATE TABLE #Result -- Used to iterate through each column on the clustered key
(
ColumnName SYSNAME
)

------------------------------------------------------------------------
-- Declare Temp Table #ClusteredIndexes being set up to hold Clustered Index info.
-- More columns are added to this table later, depending on how many columns reside on the largest composite index
------------------------------------------------------------------------
IF OBJECT_ID(N'tempdb..#ClusteredIndexes', N'U') IS NOT NULL
DROP TABLE #ClusteredIndexes;

CREATE TABLE #ClusteredIndexes
(
ClusteredIndexID INT IDENTITY(1,1), -- Identity column
SchemaName SYSNAME, -- Schema that the Table is on
TableName SYSNAME, -- Table that has the CLUSTERED INDEX
ClusteredIndexName SYSNAME, -- Name of the CLUSTERED INDEX
IsUnique CHAR(1)
CONSTRAINT ICClusteredIndexesIsUnique CHECK (IsUnique = 'Y' OR IsUnique = 'N')
CONSTRAINT IDClusteredIndexesIsUnique DEFAULT ('Y'),
IndexColumnCount INT, -- Columns that exist in the CLUSTERED INDEX
NonClusteredIndexCount INT, -- Number of NONCLUSTERED INDEXES that reside on the table
IndexSizeInBytesPerRow INT DEFAULT 0, -- Total size of the CLUSTERED INDEX in bytes
TableRowCount INT, -- Number of ROWS in the TABLE
ClusteredIndexSizeKB INT, -- Number of ROWS multiplied by IndexSize
ClustIdxSzTmsByNonClustIdxInKB INT -- CLUSTERED INDEX size mulitplied by all NONCLUSTERED indexes on that TABLE
)

------------------------------------------------------------------------
-- Iteration value is determinned by how large the largest composite clustered index is
------------------------------------------------------------------------
SET @Iteration = (SELECT MAX(C.KEY_ORDINAL)
FROM SYS.INDEXES I

JOIN SYS.OBJECTS S -- Joining on SYS.OBJECTS to get the TableName
ON S.OBJECT_ID = I.Object_ID -- to ensure the

JOIN SYS.INDEX_COLUMNS C
ON C.OBJECT_ID = I.Object_ID
AND C.INDEX_ID = I.INDEX_ID -- ** 12/03/2010 CAJ **

WHERE I.Type = @ClusteredIndexType
AND I.OBJECT_ID NOT IN (SELECT OBJECT_ID
FROM SYS.ALL_OBJECTS
WHERE TYPE = 'S') -- Not system tables
AND S.Type = 'U'
AND S.is_ms_shipped = 0)

SET @AddColumnIteration = 1;

------------------------------------------------------------------------
-- Adding the remaining columns to the #ClusteredIndex Table (determinned by largest composite index)
------------------------------------------------------------------------
WHILE (@Iteration >= @AddColumnIteration)
BEGIN
SET @Tmp = 'ALTER TABLE #ClusteredIndexes
ADD Column'+CONVERT(VARCHAR,@AddColumnIteration)+'Name VARCHAR(75),
Column'+CONVERT(VARCHAR,@AddColumnIteration)+'Type VARCHAR(25),
Column'+CONVERT(VARCHAR,@AddColumnIteration)+'SizeInBytes VARCHAR(25); '

EXEC (@Tmp);

SET @AddColumnIteration = @AddColumnIteration + 1;
END

------------------------------------------------------------------------
-- Inserting the List of Clustered Indexes into the #ClusteredIndex Table
------------------------------------------------------------------------
INSERT INTO #ClusteredIndexes (ClusteredIndexName, SchemaName, TableName, IndexColumnCount, IsUnique)
SELECT 'ClusteredIndexName' = QUOTENAME(SI.Name),
'SchemaName' = QUOTENAME(SS.Name),
'TableName' = QUOTENAME(SO.Name),
'ColumCount' = IK.IndexColumnCount,
'IsUnique' = CASE WHEN SI.is_unique = 0 THEN 'N' ELSE 'Y' END

FROM SYS.INDEXES SI

JOIN SYS.OBJECTS SO -- Joining on SYS.OBJECTS to get the TableName
ON SO.OBJECT_ID = SI.Object_ID

JOIN SYS.SCHEMAS SS -- Joining on SYS.SCHEMAS to get the SchemaName
ON SS.Schema_ID = SO.Schema_ID

JOIN ( -- Joining on a Derived view to work out how many columns exist on the clustered index
SELECT 'IndexColumnCount' = MAX(KEY_ORDINAL), OBJECT_ID, index_id
FROM SYS.INDEX_COLUMNS
GROUP BY OBJECT_ID, index_id
) AS IK
ON IK.object_id = SI.Object_ID
AND IK.index_id = SI.index_id

WHERE SI.Type = @ClusteredIndexType
AND SI.OBJECT_ID NOT IN (SELECT OBJECT_ID
FROM SYS.ALL_OBJECTS
WHERE TYPE = 'S') -- Not system tables
AND SO.Type = 'U'
AND SO.is_ms_shipped = 0

------------------------------------------------------------------------
-- Creating CLUSTERED INDEX on the Identity column on #ClusteredIndexes TEMP TABLE
------------------------------------------------------------------------
CREATE CLUSTERED INDEX XIE1ClusteredIndexes
ON #ClusteredIndexes (ClusteredIndexID)

------------------------------------------------------------------------
-- UPDATING the number of ROWS per TABLE
------------------------------------------------------------------------
UPDATE #ClusteredIndexes
SET TableRowCount = (
SELECT 'TableRowCount' = P.Rows
FROM SYS.PARTITIONS P

JOIN SYS.TABLES T -- Joining on SYS.TABLES to get the TableName
ON P.OBJECT_ID = T.Object_ID

JOIN SYS.SCHEMAS S -- Joining on SYS.SCHEMAS
ON T.Schema_id = S.Schema_id

WHERE P.index_id IN (0,1)
AND QUOTENAME(S.Name) = C.SchemaName
AND QUOTENAME(T.Name) = C.TableName)

FROM #ClusteredIndexes C

------------------------------------------------------------------------
-- UPDATING the number of NONCLUSTERED INDEXES per TABLE
------------------------------------------------------------------------
UPDATE #ClusteredIndexes
SET NonClusteredIndexCount = ISNULL((
SELECT 'NonClusteredIndexCount' = COUNT(*)
FROM SYS.INDEXES I

JOIN SYS.OBJECTS S -- Joining on SYS.OBJECTS to get the TableName
ON S.OBJECT_ID = I.Object_ID

WHERE I.type = @NonClusteredIndexType
AND QUOTENAME(S.Name) = T.TableName
GROUP BY I.object_id),0)

FROM #ClusteredIndexes T

------------------------------------------------------------------------
-- Iterate through the #ClusteredIndexes Table:
-- - Updates the column names for each clustered index along with their correct ordinal position
-- - Updating the column types of the Clustered Index
-- - Updating the max sizes of each of the columns contained within the Clustered Index
-- - Updating the total size of the Clustered Index
-- - Updating the Clustered Index Potential Max Size (Clustered index size per row * by rows on table) = ClusteredIndexSizeKB
-- - Updating the Clustered Index Potential Max that is attached to the NONCLUSTERED INDEXES (ClusteredIndexSizeKB * Non Clustered Indexes)
------------------------------------------------------------------------
DECLARE cu_PrimaryKeyColumns CURSOR FOR
SELECT ClusteredIndexID, ClusteredIndexName, SchemaName, TableName FROM #ClusteredIndexes

OPEN cu_PrimaryKeyColumns

FETCH NEXT FROM cu_PrimaryKeyColumns INTO @ClusteredIndexID, @ClusterKeyName, @SchemaName, @TableName

WHILE @@FETCH_STATUS = 0
BEGIN
SET @Iteration = (SELECT IndexColumnCount FROM #ClusteredIndexes -- Determine inner loop by how many columns comprise of the CLUSTERED INDEX
WHERE ClusteredIndexID = @ClusteredIndexID)

WHILE (@Iteration > 0) -- inner loop - one iteration per column on the CLUSTERED INDEX
BEGIN
SET @Tmp = '(SELECT QUOTENAME(C.Name)

FROM SYS.COLUMNS C

JOIN SYS.INDEX_COLUMNS IC
ON C.object_id = IC.object_id
AND C.column_id = IC.column_id

WHERE IC.KEY_ORDINAL = ' + CONVERT(VARCHAR,@Iteration) + '
AND IC.OBJECT_ID = OBJECT_ID(N''' + @SchemaName + '.' + @TableName + ''')
AND IC.index_id = 1) ' -- ** 12/03/2010 CAJ ** added @SchemaName

INSERT INTO #Result (ColumnName)
EXEC (@Tmp);

-----------------------------------------------------------------------------------
-- Updating the column names of the Clustered Index
-----------------------------------------------------------------------------------
SET @Tmp = ' UPDATE #ClusteredIndexes
SET Column'+CONVERT(VARCHAR,@Iteration)+'Name = (SELECT ColumnName FROM #Result)
WHERE '+ CONVERT(VARCHAR,@Iteration) + ' = '+CONVERT(VARCHAR,@Iteration)+'
AND ClusteredIndexID = ' + CONVERT(VARCHAR,@ClusteredIndexID) + ' '

EXEC (@Tmp);

-----------------------------------------------------------------------------------
-- Updating the column types of the Clustered Index
-----------------------------------------------------------------------------------
SET @Tmp = ' UPDATE #ClusteredIndexes
SET Column'+CONVERT(VARCHAR,@Iteration)+'Type =
(SELECT T.Name FROM SYS.TYPES T

JOIN SYS.COLUMNS C
ON C.user_type_id = T.user_type_id

WHERE QUOTENAME(C.Name) = Column'+CONVERT(VARCHAR,@Iteration)+'Name
AND C.Object_id = OBJECT_ID('''+ @SchemaName + '.' +@TableName+'''))

WHERE Column'+CONVERT(VARCHAR,@Iteration)+'Name =
(SELECT Column'+CONVERT(VARCHAR,@Iteration)+'Name
FROM #ClusteredIndexes
WHERE ClusteredIndexID = ' + CONVERT(VARCHAR,@ClusteredIndexID) + ' ) ' -- ** 12/03/2010 CAJ ** added @SchemaName
EXEC (@Tmp);

-----------------------------------------------------------------------------------
-- Updating the column sizes of the Clustered Index
-----------------------------------------------------------------------------------
SET @Tmp =' UPDATE #ClusteredIndexes
SET Column'+CONVERT(VARCHAR,@Iteration)+'SizeInBytes =
(SELECT CASE
WHEN T.system_type_id = 167 THEN (c.max_length + 2) -- VARCHAR
WHEN T.system_type_id = 231 THEN ((c.max_length * 2) + 2) -- NVARCHAR
WHEN T.system_type_id = 175 THEN (c.max_length) -- CHAR
WHEN T.system_type_id = 239 THEN (c.max_length * 2) -- NCHAR
WHEN T.system_type_id = 106 AND (c.precision < 10)THEN 5 -- DECIMAL
WHEN T.system_type_id = 106 AND (c.precision < 20 AND c.precision > 9)THEN 9 -- DECIMAL
WHEN T.system_type_id = 106 AND (c.precision < 29 AND c.precision > 19)THEN 13 -- DECIMAL
WHEN T.system_type_id = 106 AND (c.precision > 28) THEN 17 -- DECIMAL
WHEN T.system_type_id = 108 AND (c.precision < 10)THEN 5 -- NUMERIC
WHEN T.system_type_id = 108 AND (c.precision < 20 AND c.precision > 9)THEN 9 -- NUMERIC
WHEN T.system_type_id = 108 AND (c.precision < 29 AND c.precision > 19)THEN 13 -- NUMERIC
WHEN T.system_type_id = 108 AND (c.precision > 28) THEN 17 -- NUMERIC
WHEN T.system_type_id = 127 THEN 8 -- BIGINT
WHEN T.system_type_id = 56 THEN 4 -- INT
WHEN T.system_type_id = 52 THEN 2 -- SMALLINT
WHEN T.system_type_id = 48 THEN 1 -- TINYINT
WHEN T.system_type_id = 173 THEN (c.max_length) -- BINARY
WHEN T.system_type_id = 165 THEN (c.max_length + 2) -- VARBINARY
WHEN T.system_type_id = 61 THEN 8 -- DATETIME
WHEN T.system_type_id = 58 THEN 4 -- SMALLDATETIME
WHEN T.system_type_id = 104 THEN 1 -- BIT
WHEN T.system_type_id = 36 THEN 16 -- UNIQUEIDENTIFIER
WHEN T.system_type_id = 60 THEN 8 -- MONEY
WHEN T.system_type_id = 122 THEN 4 -- SMALLMONEY
WHEN T.system_type_id = 62 THEN 4 -- FLOAT
WHEN T.system_type_id = 59 THEN 4 -- REAL

-- These DataTypes are not being checked for: image, sql_variant, ntext, timestamp, xml, sysname
END

FROM SYS.TYPES T

JOIN SYS.COLUMNS C
ON C.user_type_ID = T.user_type_ID

WHERE QUOTENAME(C.Name) = Column'+CONVERT(VARCHAR,@Iteration)+'Name
AND C.Object_id = OBJECT_ID('''+ @SchemaName + '.' +@TableName+'''))

WHERE Column'+CONVERT(VARCHAR,@Iteration)+'Name =
(SELECT Column'+CONVERT(VARCHAR,@Iteration)+'Name
FROM #ClusteredIndexes
WHERE ClusteredIndexID = ' + CONVERT(VARCHAR,@ClusteredIndexID) + ')' -- ** 12/03/2010 CAJ ** added @SchemaName

EXEC (@Tmp);

-----------------------------------------------------------------------------------
-- Updating the total size of the Clustered Index
-----------------------------------------------------------------------------------
SET @Tmp = ' UPDATE #ClusteredIndexes
SET IndexSizeInBytesPerRow = IndexSizeInBytesPerRow + Column'+CONVERT(VARCHAR,@Iteration)+'SizeInBytes
WHERE Column'+CONVERT(VARCHAR,@Iteration)+'SizeInBytes IS NOT NULL
AND ClusteredIndexID = ' + CONVERT(VARCHAR,@ClusteredIndexID) + ' '

EXEC (@Tmp);

DELETE FROM #Result;

SET @Iteration = (@Iteration - 1); -- less one iteration
END -- end of Inner Loop once all columns of the CLUSTERED INDEX have been iterated through

-----------------------------------------------------------------------------------
-- Updating the Clustered Index Potential Max Size
-----------------------------------------------------------------------------------
SET @Tmp = ' UPDATE #ClusteredIndexes
SET ClusteredIndexSizeKB = (IndexSizeInBytesPerRow * TableRowCount) / 1024
WHERE ClusteredIndexID = ' + CONVERT(VARCHAR,@ClusteredIndexID) + ' '

EXEC (@Tmp);

-----------------------------------------------------------------------------------
-- Updating the Clustered Index Potential Max that is attached to the NONCLUSTERED INDEXES
-----------------------------------------------------------------------------------
SET @Tmp = ' UPDATE #ClusteredIndexes
SET ClustIdxSzTmsByNonClustIdxInKB = (NonClusteredIndexCount * ClusteredIndexSizeKB)
WHERE ClusteredIndexID = ' + CONVERT(VARCHAR,@ClusteredIndexID) + '
AND NonClusteredIndexCount <> 0 '

EXEC (@Tmp);

FETCH NEXT FROM cu_PrimaryKeyColumns INTO @ClusteredIndexID, @ClusterKeyName, @SchemaName, @TableName
END

CLOSE cu_PrimaryKeyColumns
DEALLOCATE cu_PrimaryKeyColumns

------------------------------------------------------------------------
-- PRINT out CLUSTERED INDEX''s along with other useful information
------------------------------------------------------------------------
PRINT 'The following output shows all USER TABLES CLUSTERED INDEX''s along with the columns that comprise the INDEX.'
PRINT 'Other useful information is also output, such as the potential size of the CLUSTERED INDEX according to the datatype''s that'
PRINT 'are included in the CLUSTERED INDEX. The total rows per table is calculated, which is then used to muliply against the potential'
PRINT 'CLUSTERED INDEX size to output the potential cost of disk space directly relating to the CLUSTERED INDEX.'
PRINT 'The total number of NONCLUSTERED INDEXES are also displayed per TABLE. A calculation is taken in regards to the NONCLUSTERED INDEX also'
PRINT 'since each NONCLUSTERED INDEX contains the CLUSTERED INDEX within itself therefore adding more expense to the disk space.

'
SELECT * FROM #ClusteredIndexes
ORDER BY ClustIdxSzTmsByNonClustIdxInKB DESC
--ORDER BY ClusteredIndexSizeKB DESC
--ORDER BY TableRowCount DESC
--ORDER BY IndexColumnCount DESC
--ORDER BY IndexSizeInBytesPerRow DESC

------------------------------------------------------------------------
-- PRINT out TABLES that have no CLUSTERED INDEX
------------------------------------------------------------------------
PRINT 'The following TABLE''s have no CLUSTERED INDEX at all (regarded as heaps).

'
SELECT QUOTENAME(Name)
FROM SYS.TABLES
WHERE is_ms_shipped = 0
EXCEPT
SELECT TableName
FROM #ClusteredIndexes

IF OBJECT_ID(N'tempdb..#ClusteredIndexes', N'U') IS NOT NULL
DROP TABLE #ClusteredIndexes;
IF OBJECT_ID(N'tempdb..#Result', N'U') IS NOT NULL
DROP TABLE #Result;

SET NOCOUNT OFF


RecordBycunay
Record Date21-02-2012 12:09:54
Düzenle
Kopyala
Sil