2009-09-10 2 views
15

У меня есть база данных SQL Server 2000 с примерно 220 таблицами. Эти таблицы имеют несколько внешних отношений между ними. Благодаря анализу производительности мы обнаружили, что некоторые из этих внешних ключей не содержат индексов. Вместо того, чтобы реагировать на проблемы с производительностью, я бы хотел быть активным и найти все внешние ключи, которые не содержат индексов.Как найти необработанные внешние ключи в SQL Server

Как я могу программным образом определить, какой внешний ключ отсутствует в индексах?

+0

Отсутствует или используется редко? Эта статья относится к 2005 году, но может помочь: http://blogs.msdn.com/sqlcat/archive/2006/02/13/531339.aspx –

+0

Отсутствует. Редко используется еще одна интересная оптимизация производительности, но не то, что мне здесь интересно. –

ответ

7

Вот ответ, который работает для SQL Server 2000 авторством сослуживцу:

/* 
Description: 
    This script outputs a table with all the current database un-indexed foreign keys. 

    The table has three columns (TableName , ColumnName, ForeignKeyName) 
    TableName: The table containing the un-indexed foreign key 
    ColumnName: The foreign key column that’s not indexed 
    ForeignKeyName: Name of foreign key witch column doesn’t have an index 
    */ 
DECLARE 
    @TableName varchar(255), 
    @ColumnName varchar(255), 
    @ForeignKeyName sysname 

SET NOCOUNT ON 
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 

DECLARE FKColumns_cursor CURSOR Fast_Forward FOR 
SELECT cu.TABLE_NAME, cu.COLUMN_NAME, cu.CONSTRAINT_NAME 
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS ic 
    INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu ON ic.CONSTRAINT_NAME = cu.CONSTRAINT_NAME 
WHERE ic.CONSTRAINT_TYPE = 'FOREIGN KEY' 

CREATE TABLE #temp1( 
    TableName varchar(255), 
    ColumnName varchar(255), 
    ForeignKeyName sysname 
) 

OPEN FKColumns_cursor 
FETCH NEXT FROM FKColumns_cursor INTO @TableName, @ColumnName, @ForeignKeyName 

WHILE @@FETCH_STATUS = 0 
BEGIN 

    IF (SELECT COUNT(*) 
    FROM sysobjects o  
     INNER JOIN sysindexes x ON x.id = o.id 
     INNER JOIN syscolumns c ON o.id = c.id 
     INNER JOIN sysindexkeys xk ON c.colid = xk.colid AND o.id = xk.id AND x.indid = xk.indid 
    WHERE o.type in ('U') 
     AND xk.keyno <= x.keycnt 
     AND permissions(o.id, c.name) <> 0 
     AND (x.status&32) = 0 
     AND o.name = @TableName 
     AND c.name = @ColumnName 
    ) = 0 
    BEGIN 
     INSERT INTO #temp1 SELECT @TableName, @ColumnName, @ForeignKeyName 
    END 


    FETCH NEXT FROM FKColumns_cursor INTO @TableName, @ColumnName, @ForeignKeyName 
END 
CLOSE FKColumns_cursor 
DEALLOCATE FKColumns_cursor 

SELECT * FROM #temp1 ORDER BY TableName 
0

Во-первых: укажите столбцы с ограничением внешнего ключа. Это поможет:

Query to get all foreign key constraints in SQL Server 2000

Перекрестная сравнить с sysindexes и syscolumns таблиц; поле keys в sysindexes имеет список всех ключей в индексе.

16
SELECT * 
FROM sys.foreign_keys fk 
WHERE EXISTS 
     (
     SELECT * 
     FROM sys.foreign_key_columns fkc 
     WHERE fkc.constraint_object_id = fk.object_id 
       AND NOT EXISTS 
       (
       SELECT * 
       FROM sys.index_columns ic 
       WHERE ic.object_id = fkc.parent_object_id 
         AND ic.column_id = fkc.parent_column_id 
         AND ic.index_column_id = fkc.constraint_column_id 
       ) 
     ) 

Я не имею копию SQL Server 2000 удобной, но вам может понадобиться изменить sys.foreign_key для sysforeignkeys и т.д., как и описанной here.

Этот запрос выбирает все внешние ключи, которые не имеют индекса, охватывающего все столбцы, которые содержат ключ.

Это также поддерживает несколько столбцов внешних ключей.

Это, однако, вернет ложное положительное значение, если есть составной индекс, который охватывает все столбцы, но они не являются самыми левыми столбцами этого индекса.

Как, если есть FOREIGN KEY (col2, col3) и индекс на (col1, col2, col3), это вернет, что есть индекс, несмотря на то, что этот индекс неприменим для этого внешнего ключа.

+0

Это, вероятно, делает то, что я хочу для SQL Server 2005 и более поздних версий, но я его не тестировал. Когда я его конвертирую, я отправлю эквивалент SQL Server 2000. –

+1

Я бы также имел 'AND ic.index_column_id = fkc.constraint_column_id' в NOT EXISTS, чтобы гарантировать, что индекс также находится в правильном порядке столбца индекса. В противном случае, именно то, что я использую изо дня в день – gbn

5

Построенный на коде выше уронить временную таблицу и получить скрипты для создания индексов.

/* 
Description: 

    */ 
DECLARE 
    @SchemaName varchar(255), 
    @TableName varchar(255), 
    @ColumnName varchar(255), 
    @ForeignKeyName sysname 

SET NOCOUNT ON 
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 

DECLARE FKColumns_cursor CURSOR Fast_Forward FOR 
SELECT cu.TABLE_SCHEMA, cu.TABLE_NAME, cu.COLUMN_NAME, cu.CONSTRAINT_NAME 
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS ic 
    INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE cu ON ic.CONSTRAINT_NAME = cu.CONSTRAINT_NAME 
WHERE ic.CONSTRAINT_TYPE = 'FOREIGN KEY' 

CREATE TABLE #temp1( 
    SchemaName varchar(255), 
    TableName varchar(255), 
    ColumnName varchar(255), 
    ForeignKeyName sysname 
) 

OPEN FKColumns_cursor 
FETCH NEXT FROM FKColumns_cursor INTO @SchemaName,@TableName, @ColumnName, @ForeignKeyName 

WHILE @@FETCH_STATUS = 0 
BEGIN 

    IF (SELECT COUNT(*) 
    FROM  sysobjects o  
     INNER JOIN sysindexes x ON x.id = o.id 
     INNER JOIN syscolumns c ON o.id = c.id 
     INNER JOIN sysindexkeys xk ON c.colid = xk.colid AND o.id = xk.id AND x.indid = xk.indid 
    WHERE  o.type in ('U') 
     AND xk.keyno <= x.keycnt 
     AND permissions(o.id, c.name) <> 0 
     AND (x.status&32) = 0 
     AND o.name = @TableName 
     AND c.name = @ColumnName 
    ) = 0 
    BEGIN 
     INSERT INTO #temp1 SELECT @SchemaName, @TableName, @ColumnName, @ForeignKeyName 
    END 


    FETCH NEXT FROM FKColumns_cursor INTO @SchemaName,@TableName, @ColumnName, @ForeignKeyName 
END 
CLOSE FKColumns_cursor 
DEALLOCATE FKColumns_cursor 

SELECT 'CREATE INDEX IDX_' + ForeignKeyName + ' ON ' + SchemaName + '.' + TableName + '(' + ColumnName +')' 
FROM #temp1 
ORDER BY TableName 

drop table #temp1 
0

Примечание: Это для SQL Server 2005+, но это был единственный вопрос, который я нашел о Эта тема.

--Finds foreign keys without indexes 
--How to interpret: 
--When we delete frpm PkTable, it checks FkColumn for the PkId we are deleting. 
--So if FkTable doesn't have an index on FkColumn, then we cannot delete a row from PkTable because it is too slow. 
SELECT rt.name as PkTableName, rc.name as PkColumnName, 
fk.name FkName, t.name as FkTableName, c.name as FkColumnName, ddps.row_count, i.name as IndexName 
FROM sys.foreign_key_columns fkc 
inner join sys.foreign_keys fk on fkc.constraint_object_id = fk.object_id 
inner join sys.tables t on fkc.parent_object_id = t.object_id 
inner join sys.columns c on fkc.parent_object_id = c.object_id and fkc.parent_column_id = c.column_id 
inner join sys.tables rt on fkc.referenced_object_id = rt.object_id 
inner join sys.columns rc on fkc.referenced_object_id = rc.object_id and fkc.referenced_column_id = rc.column_id 
inner join sys.indexes ri on t.object_id = ri.object_id 
inner JOIN sys.dm_db_partition_stats AS ddps ON ri.OBJECT_ID = ddps.OBJECT_ID AND ri.index_id = ddps.index_id 
left join sys.index_columns ic on ic.object_id = t.object_id and ic.column_id = c.column_id 
left join sys.indexes i on ic.object_id = i.object_id and i.index_id = ic.index_id 
where ri.index_id < 2 and i.index_id is null and ddps.row_count > 0 
order by 
--PkTableName, 
ddps.row_count desc