¿Cómo puedo encontrar claves externas no indexadas en SQL Server?

votos
15

Tengo una base de datos SQL Server 2000 con aproximadamente 220 tablas. Estas tablas tienen un número de relaciones de clave externa entre ellos. A través del análisis de rendimiento, descubrimos que a varias de estas relaciones de clave foránea les faltan índices. En lugar de ser reactivo con los problemas de rendimiento, me gustaría ser proactivo y encontrar todas las claves externas que faltan índices.

¿Cómo puedo determinar mediante programación qué clave externa faltan los índices?

Publicado el 10/09/2009 a las 16:51
fuente por usuario
En otros idiomas...                            


6 respuestas

votos
0

En primer lugar: enumere las columnas con una restricción de clave externa. Esto ayudará:

Consulta para obtener todas las restricciones de clave externa en SQL Server 2000

Comparación cruzada con sysindexesy syscolumnstablas; el keyscampo en sysindexestiene una lista de todas las claves en el índice.

Respondida el 10/09/2009 a las 17:08
fuente por usuario

votos
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
                )
        )

No tengo una copia SQL Server 2000útil, pero es posible que deba cambiar sys.foreign_keya sysforeignkeysetc., como se describe here.

Esta consulta selecciona todas las claves foráneas que no tienen un índice que cubra todas las columnas que componen la clave.

Esto también admite claves externas de varias columnas.

Sin embargo, esto arrojará un falso positivo si existe un índice compuesto que cubra todas las columnas, pero no son las columnas más a la izquierda en este índice.

Al igual que, si hay un FOREIGN KEY (col2, col3)y un índice (col1, col2, col3), esto devolverá que hay un índice a pesar de que este índice no se puede utilizar para esta clave externa.

Respondida el 10/09/2009 a las 17:18
fuente por usuario

votos
7

Aquí hay una respuesta que funciona para SQL Server 2000 creado por un compañero de trabajo:

/*
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 IGNORE  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
Respondida el 11/09/2009 a las 18:58
fuente por usuario

votos
5

Construida en el código anterior para eliminar la tabla temporal y obtener secuencias de comandos para crear los índices.

   /*
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 IGNORE  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 
Respondida el 04/03/2011 a las 21:26
fuente por usuario

votos
1

En mi post "secuencia de comandos SQL para crear índices de claves externas" He puesto enlaces a 2 implementaciones: paul_nielsen ‘s y tklimczak de (la cuenta para sqlservercentral se requiere)

Respondida el 05/06/2011 a las 14:51
fuente por usuario

votos
0

Nota: Esto es para SQL Server 2005 + pero esta fue la única pregunta que encontré sobre este tema.

--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
Respondida el 08/11/2017 a las 18:33
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more