2009-09-10 55 views
15

我有一個包含大約220個表的SQL Server 2000數據庫。這些表具有它們之間的數字外鍵關係。通過性能分析,我們發現一些這些外鍵關係是缺少索引。我不希望被性能問題所困擾,而是想積極主動地找到所有缺少索引的外鍵。如何在SQL Server中查找未索引的外鍵

如何以編程方式確定哪些外鍵缺少索引?

+0

缺失或很少使用?這篇文章是爲2005年,但可以幫助:http://blogs.msdn.com/sqlcat/archive/2006/02/13/531339.aspx – 2009-09-10 15:57:59

+0

缺少。很少使用的是另一個有趣的性能優化,但不是我在這裏感興趣的。 – 2009-09-10 16:01:38

回答

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 
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_keysysforeignkeys等,像描述here

該查詢選擇所有沒有索引的外鍵,其中包含構成密鑰的所有列。

這也支持多列外鍵。

但是,如果存在覆蓋所有列但不是此索引中最左列的組合索引,則會返回誤報。

一樣,如果有一個FOREIGN KEY (col2, col3)(col1, col2, col3)索引,這將返回有,儘管這一指數是不能用於本外鍵索引。

+0

這可能是我想要的SQL Server 2005和更高版本,但我沒有測試過它。當我將其轉換時,我會發布SQL Server 2000等價物。 – 2009-09-10 17:30:56

+1

我還會在NOT EXISTS中使用'AND ic.index_column_id = fkc.constraint_column_id'來確保索引也處於正確的索引鍵列順序。否則,正是我每天都在使用的東西 – gbn 2013-04-25 09:05:30

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