Get Index Columns
Applicability:
SQL Server 2000: Not Supported
SQL Server 2005: Tested
SQL Server 2008: Tested
SQL Server 2008R2: Tested
SQL Server 2012: Not Tested
Credits:
Author: Unknown/ChillyDBA
Date: 7 Nov 2010/7 Jul 2012
20 Jun 2014 - ChillyDBA - Bracketed the returned index key columns to allow for spaces in column names
Description
Returns a list of indexes for one or all tables in the database (depending on input parameter).
All index key columns are returned.
If a table has no indexes, then one row with index type HEAP is returned.
I converted to a table-valued function and augmented the columns returned to included attributes that are consumed by the SP that produces the CREATE/DROP Index TSQL.
Code
Function:
DROP FUNCTION dbo.udf_GetIndexColumns
GO
CREATE FUNCTION dbo.udf_GetIndexColumns(@TableName SYSNAME = NULL)
RETURNS @udf_IndexColumns TABLE
(
DatabaseName SYSNAME NULL,
TableSchema SYSNAME NULL,
TableName SYSNAME NULL,
IndexName SYSNAME NULL,
IndexType VARCHAR(20),
IndexColumns VARCHAR(1000),
IndexColumns_SO VARCHAR(1000),
IncludedColumns VARCHAR(1000),
IncludedColumns_SO VARCHAR(1000),
StatisticsLastUpdated DATETIME,
FileGroupName SYSNAME NULL,
Fill_Factor INT,
IsPadded INT,
NoRecompute INT,
AllowRowLocks INT,
AllowPageLocks INT
)
AS
/***************************************************************
Purpose: Lists the indexes and their columns
Also lists hepa tables, but as every table has a heap, these
are restricted to tables with no other index definitions
The following DMVs are used:
sys.indexes - list all index details in the db
sys.index_columns - list all index column details in the db
sys.tables - list all table details in the db
sys.all_columns - list all columns detail in the db
Due to the use of CTE, this is valid for SQL 20065 and greater
For conversion to SQL 2000, the CTE code could be used to populate a temp table
Author: Unknown
History: 7 Nov 2010 - Initial Issue
6 Jul 2012 - ChillyDBA - Converted to function and enhanced with schema and stats date
7 Jul 2012 - ChillyDBA - Augmented results to encompass included columns
plus versions of the columns lists with sort order, plus
some essential index creation flags.
23 Jun 2014 - ChillyDBA - added [] around column names returned to insulate against special characters or spaces in names.
****************************************************************/
BEGIN
WITH AllIndexes_CTE
(
TableSchema,
TableName,
IndexName,
IndexType ,
IndexColumns,
IndexColumns_SO,
IncludedColumns,
IncludedColumns_SO,
StatisticsLastUpdated,
FileGroupName,
Fill_Factor,
IsPadded,
NoRecompute,
AllowRowLocks,
AllowPageLocks
)
AS
-- identify indexes of all types
(
SELECT
SCHEMA_NAME(Tab.schema_id) AS TableSchema,
Tab.[name] AS TableName,
IND.[name] AS IndexName,
IND.Type_Desc AS IndexType,
SUBSTRING (
(
SELECT ', [' + AC.name + ']'
FROM sys.tables AS T
INNER JOIN sys.indexes I
ON T.OBJECT_ID = I.[object_id]
INNER JOIN sys.index_columns IC
ON I.OBJECT_ID = IC.[object_id]
AND I.index_id = IC.[index_id]
INNER JOIN sys.all_columns AC
ON T.OBJECT_ID = AC.[object_id]
AND IC.column_id = AC.[column_id]
WHERE Ind.OBJECT_ID = i.[object_id]
AND ind.index_id = i.index_id
AND IC.is_included_column = 0
ORDER BY IC.key_ordinal
FOR XML PATH('')
), 2, 8000
) AS IndexColumns,
SUBSTRING (
(
SELECT ', ['
+ AC.name
+ CASE
WHEN ic.is_descending_key = 1 THEN '] DESC'
ELSE '] ASC'
END
FROM sys.tables AS T
INNER JOIN sys.indexes I
ON T.OBJECT_ID = I.[object_id]
INNER JOIN sys.index_columns IC
ON I.OBJECT_ID = IC.[object_id]
AND I.index_id = IC.[index_id]
INNER JOIN sys.all_columns AC
ON T.OBJECT_ID = AC.[object_id]
AND IC.column_id = AC.[column_id]
WHERE Ind.OBJECT_ID = i.[object_id]
AND ind.index_id = i.index_id
AND IC.is_included_column = 0
ORDER BY IC.key_ordinal
FOR XML PATH('')
), 2, 8000
) AS IndexColumns_SO,
SUBSTRING (
(
SELECT ', [' + AC.name + ']'
FROM sys.tables AS T
INNER JOIN sys.inddexes I
ON T.OBJECT_ID = I.[object_id]
INNER JOIN sys.index_columns IC
ON I.OBJECT_ID = IC.[object_id]
AND I.index_id = IC.[index_id]
INNER JOIN sys.all_columns AC
ON T.OBJECT_ID = AC.[object_id]
AND IC.column_id = AC.[column_id]
WHERE Ind.OBJECT_ID = i.[object_id]
AND ind.index_id = i.index_id
AND IC.is_included_column = 1
ORDER BY IC.key_ordinal
FOR XML PATH('')
), 2, 8000
) AS IncludedColumns,
SUBSTRING (
(
SELECT ', [' + AC.name + ']' -- no sort order on an included column
FROM sys.tables AS T
INNER JOIN sys.indexes I
ON T.OBJECT_ID = I.[object_id]
INNER JOIN sys.index_columns IC
ON I.OBJECT_ID = IC.[object_id]
AND I.index_id = IC.[index_id]
INNER JOIN sys.all_columns AC
ON T.OBJECT_ID = AC.[object_id]
AND IC.column_id = AC.[column_id]
WHERE Ind.OBJECT_ID = i.[object_id]
AND ind.index_id = i.index_id
AND IC.is_included_column = 1
ORDER BY IC.key_ordinal
FOR XML PATH('')
), 2, 8000
) AS IncludedColumns_SO,
STATS_DATE(TAB.OBJECT_ID, IND.Index_ID) AS StatisticsLastUpdated,
fg.name AS FileGroupName,
CASE
WHEN Ind.Fill_Factor = 0 THEN 100
ELSE Ind.Fill_Factor
END AS Fill_Factor,
Ind.Is_Padded AS IsPadded,
s.No_Recompute AS NoRecompute,
Ind.allow_row_locks AS AllowRowLocks,
Ind.allow_page_locks AS AllowPageLocks
FROM sys.indexes Ind
INNER JOIN sys.stats s
ON Ind.name = s.name
INNER JOIN sys.tables AS Tab
ON Tab.OBJECT_ID = Ind.OBJECT_ID
LEFT JOIN sys.filegroups FG
ON Ind.data_space_id = FG.data_space_id
WHERE tab.Name = ISNULL(@TableName, tab.Name)
AND Ind.Name IS NOT NULL
AND INDEXPROPERTY(Ind.OBJECT_ID, Ind.Name, 'IsStatistics') = 0
)
INSERT @udf_IndexColumns
(
DatabaseName,
TableSchema,
TableName,
IndexName,
IndexType,
IndexColumns,
IndexColumns_SO,
IncludedColumns,
IncludedColumns_SO,
StatisticsLastUpdated,
FileGroupName,
Fill_Factor,
IsPadded,
NoRecompute,
AllowRowLocks,
AllowPageLocks
)
-- list the clustered and nonclustered
SELECT
DB_NAME() AS DatabaseName,
cte1.TableSchema AS TableSchema,
cte1.TableName AS TableName,
cte1.IndexName AS IndexName,
cte1.IndexType AS IndexName,
cte1.IndexColumns AS IndexColumns,
cte1.IndexColumns_SO AS IndexColumns_SO,
cte1.IncludedColumns AS IncludedColumns,
cte1.IncludedColumns_SO AS IncludedColumns_SO,
cte1.StatisticsLastUpdated AS StatisticsLastUpdated,
cte1.FileGroupName AS FileGroupName,
cte1.Fill_Factor AS Fill_Factor,
cte1.IsPadded AS IsPadded ,
cte1.NoRecompute AS NoRecompute ,
cte1.AllowRowLocks AS AllowRowLocks ,
cte1.AllowPageLocks AS AllowPageLocks
FROM AllIndexes_CTE cte1
WHERE IndexType IN ('CLustered', 'NonClustered')
UNION
-- list the heaps, but only where there are no other indexes ie 0 indexes on table and only a heap entry
SELECT
DB_NAME() AS DatabaseName,
cte2.TableSchema AS TableSchema,
cte2.TableName AS TableName,
cte2.IndexName AS IndexName,
cte2.IndexType AS IndexName,
cte2.IndexColumns AS IndexColumns ,
cte2.IndexColumns_SO AS IndexColumns_SO ,
cte2.IncludedColumns AS IncludedColumns,
cte2.IncludedColumns_SO AS IncludedColumns_SO,
NULL AS StatisticsLastUpdated,
cte2.FileGroupName AS FileGroupName,
cte2.Fill_Factor AS Fill_Factor,
cte2.IsPadded AS IsPadded ,
cte2.NoRecompute AS NoRecompute ,
cte2.AllowRowLocks AS AllowRowLocks ,
cte2.AllowPageLocks AS AllowPageLocks
FROM AllIndexes_CTE cte2
WHERE IndexType IN ('Heap')
AND (SELECT COUNT(*) FROM AllIndexes_CTE cte3 WHERE cte3.TableName = cte2.TableName) = 1
ORDER BY cte1.TableName, cte1.IndexType, cte1.IndexName
RETURN
END
/*
USE AdventureWorksDW
GO
SELECT * FROM dbo.udf_GetIndexColumns(NULL)
SELECT * FROM dbo.udf_GetIndexColumns('DimCustomer')
SELECT * FROM dbo.udf_GetIndexColumns('Calendar')
*/
Test Code:
USE AdventureWorksDW
GO
SELECT * FROM dbo.udf_GetIndexColumns(NULL)
SELECT * FROM dbo.udf_GetIndexColumns('DimCustomer')