2009-09-10 25 views
15

Tôi có cơ sở dữ liệu SQL Server 2000 với khoảng 220 bảng. Các bảng này có một số mối quan hệ khóa ngoài giữa chúng. Thông qua phân tích hiệu suất, chúng tôi đã phát hiện một số các mối quan hệ khóa ngoại là thiếu các chỉ mục. Thay vì phản ứng với các vấn đề về hiệu năng, tôi muốn chủ động và tìm tất cả các khóa ngoại đang thiếu các chỉ mục.Làm cách nào tôi có thể tìm thấy khóa ngoài không được lập chỉ mục trong SQL Server

Làm cách nào tôi có thể lập trình xác định khóa ngoại nào đang thiếu chỉ mục?

+0

Thiếu hoặc hiếm khi được sử dụng? Bài viết này dành cho năm 2005, nhưng có thể giúp: http://blogs.msdn.com/sqlcat/archive/2006/02/13/531339.aspx –

+0

Thiếu. Hiếm khi được sử dụng là một tối ưu hóa hiệu suất thú vị khác, nhưng không phải là những gì tôi quan tâm ở đây. –

Trả lời

7

Dưới đây là một câu trả lời mà làm việc cho SQL Server 2000 là tác giả của một đồng nghiệp:

/* 
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 
       ) 
     ) 

Tôi không có một bản sao của SQL Server 2000 tiện dụng, nhưng bạn có thể cần phải thay đổi sys.foreign_key-sysforeignkeys vv như mô tả here.

Truy vấn này chọn tất cả khóa ngoại không có chỉ mục bao gồm tất cả các cột bao gồm khóa.

Điều này cũng hỗ trợ các khóa ngoại đa cột.

Điều này, tuy nhiên, sẽ trả về giá trị dương nếu có chỉ mục tổng hợp bao gồm tất cả các cột nhưng chúng không phải là cột ngoài cùng bên trái trong chỉ mục này.

Giống như, nếu có FOREIGN KEY (col2, col3) và chỉ mục trên (col1, col2, col3), điều này sẽ trả về rằng có chỉ mục mặc dù thực tế chỉ mục này không thể sử dụng được cho khóa ngoại này.

+0

Điều này có thể làm những gì tôi muốn cho SQL Server 2005 và sau đó, nhưng tôi đã không thử nghiệm nó. Tôi sẽ đăng SQL Server 2000 tương đương khi tôi chuyển đổi nó. –

+1

Tôi cũng sẽ có 'AND ic.index_column_id = fkc.constraint_column_id' trong NOT EXISTS để đảm bảo chỉ mục nằm trong đúng thứ tự cột chỉ mục chính xác. Nếu không, chính xác những gì tôi sử dụng hàng ngày – gbn

5

Được xây dựng trên mã trên để thả các bảng temp và nhận được kịch bản để tạo ra các chỉ mục.

/* 
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

Lưu ý: Đây là dành cho SQL Server 2005 + nhưng đây là câu hỏi duy nhất tôi tìm thấy về chủ đề này.

--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 
Các vấn đề liên quan