简体   繁体   中英

How to get index names for all databases by database name on a SQL Server

The title pretty much sums up what I am looking for. So I am looking to get some meta information for sql indexes for eg. TableName, SchemaName, DatabaseName.

SELECT 
 TableName = t.name,
 IndexName = ind.name,
 IndexId = ind.index_id,
 ColumnId = ic.index_column_id,
 ColumnName = col.name,
 SchemaName = s.name
FROM 
 sys.indexes ind 
INNER JOIN 
 sys.index_columns ic ON  ind.object_id = ic.object_id and ind.index_id = ic.index_id 
INNER JOIN 
 sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id 
INNER JOIN 
 sys.tables t ON ind.object_id = t.object_id
INNER JOIN
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
 ind.name = 'testindex'
ORDER BY 
 t.name, ind.name, ind.index_id, ic.index_column_id;

So far I have managed to get TableName and SchemaName for an index, however I don't know how to get the database name.

Using Aaron Bertrand's script for making a more reliable and flexible sp_MSforeachdb , you can use your query with a modification.

You will need to use this, or a cursor, or some other method to iterate over all databases on your server. This is one of the bests I have seen, since Microsoft's version can skip databases as Aaron points out.

sp_foreachdb Method

CREATE PROCEDURE dbo.sp_foreachdb
    @command NVARCHAR(MAX),
    @replace_character NCHAR(1) = N'?',
    @print_dbname BIT = 0,
    @print_command_only BIT = 0,
    @suppress_quotename BIT = 0,
    @system_only BIT = NULL,
    @user_only BIT = NULL,
    @name_pattern NVARCHAR(300) = N'%', 
    @database_list NVARCHAR(MAX) = NULL,
    @recovery_model_desc NVARCHAR(120) = NULL,
    @compatibility_level TINYINT = NULL,
    @state_desc NVARCHAR(120) = N'ONLINE',
    @is_read_only BIT = 0,
    @is_auto_close_on BIT = NULL,
    @is_auto_shrink_on BIT = NULL,
    @is_broker_enabled BIT = NULL
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE
        @sql NVARCHAR(MAX),
        @dblist NVARCHAR(MAX),
        @db NVARCHAR(300),
        @i INT;

    IF @database_list > N''
    BEGIN
        ;WITH n(n) AS 
        (
            SELECT ROW_NUMBER() OVER (ORDER BY s1.name) - 1
            FROM sys.objects AS s1 
            CROSS JOIN sys.objects AS s2
        )
        SELECT @dblist = REPLACE(REPLACE(REPLACE(x,'</x><x>',','),
        '</x>',''),'<x>','')
        FROM 
        (
            SELECT DISTINCT x = 'N''' + LTRIM(RTRIM(SUBSTRING(
            @database_list, n,
            CHARINDEX(',', @database_list + ',', n) - n))) + ''''
            FROM n WHERE n <= LEN(@database_list)
            AND SUBSTRING(',' + @database_list, n, 1) = ','
            FOR XML PATH('')
        ) AS y(x);
    END

    CREATE TABLE #x(db NVARCHAR(300));

    SET @sql = N'SELECT name FROM sys.databases WHERE 1=1'
        + CASE WHEN @system_only = 1 THEN 
            ' AND database_id IN (1,2,3,4)' 
            ELSE '' END
        + CASE WHEN @user_only = 1 THEN 
            ' AND database_id NOT IN (1,2,3,4)' 
            ELSE '' END
        + CASE WHEN @name_pattern <> N'%' THEN 
            ' AND name LIKE N''%' + REPLACE(@name_pattern, '''', '''''') + '%''' 
            ELSE '' END
        + CASE WHEN @dblist IS NOT NULL THEN 
            ' AND name IN (' + @dblist + ')' 
            ELSE '' END
        + CASE WHEN @recovery_model_desc IS NOT NULL THEN
            ' AND recovery_model_desc = N''' + @recovery_model_desc + ''''
            ELSE '' END
        + CASE WHEN @compatibility_level IS NOT NULL THEN
            ' AND compatibility_level = ' + RTRIM(@compatibility_level)
            ELSE '' END
        + CASE WHEN @state_desc IS NOT NULL THEN
            ' AND state_desc = N''' + @state_desc + ''''
            ELSE '' END
        + CASE WHEN @is_read_only IS NOT NULL THEN
            ' AND is_read_only = ' + RTRIM(@is_read_only)
            ELSE '' END
        + CASE WHEN @is_auto_close_on IS NOT NULL THEN
            ' AND is_auto_close_on = ' + RTRIM(@is_auto_close_on)
            ELSE '' END
        + CASE WHEN @is_auto_shrink_on IS NOT NULL THEN
            ' AND is_auto_shrink_on = ' + RTRIM(@is_auto_shrink_on)
            ELSE '' END
        + CASE WHEN @is_broker_enabled IS NOT NULL THEN
            ' AND is_broker_enabled = ' + RTRIM(@is_broker_enabled)
        ELSE '' END;

        INSERT #x EXEC sp_executesql @sql;

        DECLARE c CURSOR 
            LOCAL FORWARD_ONLY STATIC READ_ONLY
            FOR SELECT CASE WHEN @suppress_quotename = 1 THEN 
                    db
                ELSE
                    QUOTENAME(db)
                END 
            FROM #x ORDER BY db;

        OPEN c;

        FETCH NEXT FROM c INTO @db;

        WHILE @@FETCH_STATUS = 0
        BEGIN
            SET @sql = REPLACE(@command, @replace_character, @db);

            IF @print_command_only = 1
            BEGIN
                PRINT '/* For ' + @db + ': */'
                + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)
                + @sql 
                + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10);
            END
            ELSE
            BEGIN
                IF @print_dbname = 1
                BEGIN
                    PRINT '/* ' + @db + ' */';
                END

                EXEC sp_executesql @sql;
            END

            FETCH NEXT FROM c INTO @db;
    END

    CLOSE c;
    DEALLOCATE c;
END

Execution of Proc

EXEC sp_foreachdb
@command = 'SELECT
 DBNAME = ''?'', 
 TableName = t.name,
 IndexName = ind.name,
 IndexId = ind.index_id,
 ColumnId = ic.index_column_id,
 ColumnName = col.name,
 SchemaName = s.name
FROM 
 ?.sys.indexes ind 
INNER JOIN 
 ?.sys.index_columns ic ON  ind.object_id = ic.object_id and ind.index_id = ic.index_id 
INNER JOIN 
 ?.sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id 
INNER JOIN 
 ?.sys.tables t ON ind.object_id = t.object_id
INNER JOIN
 ?.sys.schemas s ON t.schema_id = s.schema_id
ORDER BY 
 t.name, ind.name, ind.index_id, ic.index_column_id'
 ,@print_command_only = 0

No Proc & No Cursor Method

Thanks to Sean Lange for reminding me how, you can use a hack of variable concatenation to not use a cursor, or a procedure, and accomplish the same task.

declare @sql varchar(max) = ''

select @sql = @sql 
        + 'use ' 
        + quotename(name) 
        + char(13) 
        + 'select DBNAME = ''' 
        + name 
        + ''',  
                TableName = t.name,
                IndexName = ind.name,
                IndexId = ind.index_id,
                ColumnId = ic.index_column_id,
                ColumnName = col.name,
                SchemaName = s.name
            FROM 
                sys.indexes ind 
            INNER JOIN 
                sys.index_columns ic ON  ind.object_id = ic.object_id and ind.index_id = ic.index_id 
            INNER JOIN 
                sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id 
            INNER JOIN 
                sys.tables t ON ind.object_id = t.object_id
            INNER JOIN
                sys.schemas s ON t.schema_id = s.schema_id; '  
        + char(13) 
        from sys.databases
        where state = 0
exec(@sql)

I use something like this.

select t.name, i.name, t.object_id,
STUFF((
    select N' , ' + name 
    from sys.columns c
    join sys.index_columns ic on ic.object_id = c.object_id and ic.column_id = c.column_id
    where ic.object_id = i.object_id and c.object_id = i.object_id
    and ic.index_id = i.index_id
    and ic.is_included_column=0
    order by ic.is_included_column, ic.key_ordinal

    FOR XML PATH(''),TYPE).value('text()[1]','nvarchar(max)'),1,2,N''
) IndexKeyColumns
,
STUFF((

    select N' , ' + name 
    from sys.columns c
    join sys.index_columns ic on ic.object_id = c.object_id and ic.column_id = c.column_id
    where ic.object_id = i.object_id and c.object_id = i.object_id
    and ic.index_id = i.index_id
    and ic.is_included_column=1
    order by ic.is_included_column, ic.key_ordinal
    FOR XML PATH(''),TYPE).value('text()[1]','nvarchar(max)'),1,2,N''
) IncludedColumns
from sys.indexes i
join sys.tables t on t.object_id = i.object_id
where i.type in (1,2)
and i.is_primary_key = 0
order by t.name, i.name

You can execute against your dbs with dynamic sql

declare myDatabases cursor for 
    select name from sys.databases
    where name in ('db1','db2') -- leave this out for all of them
    order by name


declare @tempDBName varchar(200)
open myDatabases

fetch next from mydatabases into @tempDBName
while @@FETCH_STATUS = 0
begin

    declare @sql nvarchar(max)

    set @sql = N'
        declare @innersql nvarchar(max) = ''use ''+@tempDBName
        set @innersql = @innersql +''
        select db_name()
        ''
    exec (@innersql)
    '
    exec sp_executesql @sql,N'@tempDBName varchar(200)',@tempDBName
    fetch next from myDatabases into @tempDBName
end

close myDatabases
deallocate myDatabases

Putting it together with your query, rather than mine (many quotes to sort out) it would be something like this... pull out the where clause line entirely should get you your selection

declare myDatabases cursor for 
    select name from sys.databases
    where name in ('db1','db2')
    order by name


declare @tempDBName varchar(200)

open myDatabases

fetch next from mydatabases into @tempDBName
while @@FETCH_STATUS = 0
begin

    declare @sql nvarchar(max)

    set @sql = N'
        declare @innersql nvarchar(max) = ''use ''+@tempDBName
        set @innersql = @innersql +
        '''
        +

        --select db_name()
        '
        SELECT 
         TableName = t.name,
         IndexName = ind.name,
         IndexId = ind.index_id,
         ColumnId = ic.index_column_id,
         ColumnName = col.name,
         SchemaName = s.name
        FROM 
         sys.indexes ind 
        INNER JOIN 
         sys.index_columns ic ON  ind.object_id = ic.object_id and ind.index_id = ic.index_id 
        INNER JOIN 
         sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id 
        INNER JOIN 
         sys.tables t ON ind.object_id = t.object_id
        INNER JOIN
            sys.schemas s ON t.schema_id = s.schema_id
        WHERE 
         ind.name = ''''testindex''''
        ORDER BY 
         t.name, ind.name, ind.index_id, ic.index_column_id;
        '


        +
        '''
    exec (@innersql)
    '
    exec sp_executesql @sql,N'@tempDBName varchar(200)',@tempDBName
    fetch next from myDatabases into @tempDBName
end

close myDatabases
deallocate myDatabases

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM