Cambiar el índice referenciado para una clave externa

9

Tengo algo como esto:

CREATE TABLE T1 (
    Id INT
    ...
    ,Constraint [PK_T1] PRIMARY KEY CLUSTERED [Id]
)

CREATE TABLE T2 (
    ....
    ,T1_Id INT NOT NULL
    ,CONSTRAINT [FK_T2_T1] FOREIGN KEY (T1_Id) REFERENCES T1(Id)
)

Por razones de rendimiento (y punto muerto) creé un nuevo índice en T1

CREATE UNIQUE NONCLUSTERED INDEX IX_T1_Id ON T1 (Id)

Pero si compruebo a qué índice hace referencia el FK, sigue haciendo referencia al índice agrupado

select
    ix.index_id,
    ix.name as index_name,
    ix.type_desc as index_type_desc,
    fk.name as fk_name
from sys.indexes ix
    left join sys.foreign_keys fk on
        fk.referenced_object_id = ix.object_id
        and fk.key_index_id = ix.index_id
        and fk.parent_object_id = object_id('T2')
where ix.object_id = object_id('T1');

Si elimino la restricción y creo de nuevo, hace referencia al índice no agrupado, pero esto lleva a verificar todo t2 FK nuevamente.

¿Hay alguna forma de cambiar esto para que FK_T2_T1 use IX_T1_Id en lugar de PK_T1 sin soltar el FK y bloquear la tabla en la comprobación de FK?

¡Gracias!

Mariano G
fuente
Hubo una discusión relevante aquí .
i-one

Respuestas:

6

Bueno, luego de seguir buscando encontré este artículo

A diferencia de una consulta normal, no recogerá un nuevo índice debido a la actualización de estadísticas, la creación de un nuevo índice o incluso el reinicio de un servidor. La única forma en que sé que un enlace FK a un índice diferente es soltar y volver a crear el FK, lo que le permite seleccionar automáticamente el índice sin opciones para controlarlo manualmente.

Con lo cual, a menos que alguien pueda decir lo contrario, tendré que buscar una ventana de tiempo para realizar esta tarea.

Gracias

Mariano G
fuente
2

Después de leer MS DOCS aquí .

Para modificar una clave foránea

Para modificar una restricción FOREIGN KEY utilizando Transact-SQL, primero debe eliminar la restricción FOREIGN KEY existente y luego volver a crearla con la nueva definición. Para obtener más información, vea Eliminar relaciones de clave externa y Crear relaciones de clave externa.

En su caso, creo que agregue un nuevo FK y elimine el anterior. Para deshabilitar el escaneo puede usar la NO CHECKopción

--DROP TABLE T2
--DROP TABLE T1


CREATE TABLE T1 (
    [Id] INT,
    [NAME] varchar(100), CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED (id))

CREATE TABLE T2 (
    t2_id int,
    T1_Id INT NOT NULL
    ,CONSTRAINT [FK_T2_T1] FOREIGN KEY (T1_Id) REFERENCES T1(Id)
)


CREATE UNIQUE NONCLUSTERED INDEX IX_T1_Id ON T1 (Id)


select
    ix.index_id,
    ix.name as index_name,
    ix.type_desc as index_type_desc,
    fk.name as fk_name
from sys.indexes ix
    left join sys.foreign_keys fk on
        fk.referenced_object_id = ix.object_id
        and fk.key_index_id = ix.index_id
        and fk.parent_object_id = object_id('T2')
where ix.object_id = object_id('T1');



╔══════════╦════════════╦═════════════════╦══════════╗
 index_id  index_name  index_type_desc  fk_name  
╠══════════╬════════════╬═════════════════╬══════════╣
        1  PK_T1       CLUSTERED        FK_T2_T1 
        2  IX_T1_Id    NONCLUSTERED     NULL     
╚══════════╩════════════╩═════════════════╩══════════╝




 ALTER TABLE T2
    WITH NOCHECK 
    ADD CONSTRAINT [FK_T2_T1_NEW] FOREIGN KEY(T1_Id)
    REFERENCES T1(Id)

select
    ix.index_id,
    ix.name as index_name,
    ix.type_desc as index_type_desc,
    fk.name as fk_name
from sys.indexes ix
    left join sys.foreign_keys fk on
        fk.referenced_object_id = ix.object_id
        and fk.key_index_id = ix.index_id
        and fk.parent_object_id = object_id('T2')
where ix.object_id = object_id('T1');


╔══════════╦════════════╦═════════════════╦══════════════╗
 index_id  index_name  index_type_desc    fk_name    
╠══════════╬════════════╬═════════════════╬══════════════╣
        1  PK_T1       CLUSTERED        FK_T2_T1     
        2  IX_T1_Id    NONCLUSTERED     FK_T2_T1_NEW 
╚══════════╩════════════╩═════════════════╩══════════════╝   

ALTER TABLE T2  
DROP CONSTRAINT FK_T2_T1 

select
    ix.index_id,
    ix.name as index_name,
    ix.type_desc as index_type_desc,
    fk.name as fk_name
from sys.indexes ix
    left join sys.foreign_keys fk on
        fk.referenced_object_id = ix.object_id
        and fk.key_index_id = ix.index_id
        and fk.parent_object_id = object_id('T2')
where ix.object_id = object_id('T1');


╔══════════╦════════════╦═════════════════╦══════════════╗
 index_id  index_name  index_type_desc    fk_name    
╠══════════╬════════════╬═════════════════╬══════════════╣
        1  PK_T1       CLUSTERED        NULL         
        2  IX_T1_Id    NONCLUSTERED     FK_T2_T1_NEW 
╚══════════╩════════════╩═════════════════╩══════════════╝

Vea si esto funciona, lo que estoy intentando es agregar un FK más para que el nuevo esté vinculado al nuevo índice creado y descarte el FK anterior. Sé que la pregunta no es descartar la existente, pero ver si esta opción te ayudará.

Además, según los comentarios de Max Vernon: "la opción WITH NOCHECK evitará que el optimizador confíe en la clave externa. En algún momento, tendría que alterar la clave externa para que sea confiable usando ALTER TABLE ... CON CHEQUEO "

La NOCHECKúnica serán ignorados en el momento de la creación, sino para hacer cumplir la integridad contraint ha ejecutado esto en algún punto del tiempo.

Biju jose
fuente
la WITH NOCHECKopción evitará que el optimizador confíe en la clave externa. En algún momento, tendrías que alterar la clave externa para que sea confiable usarlaALTER TABLE ... WITH CHECK
Max Vernon
@MaxVernon, así que eso significa que no tenemos una opción
Biju jose
correcto. La única forma de obtener la clave externa para usar el nuevo índice es recrear la clave externa con la opción VERIFICAR intacta.
Max Vernon el
@max Vernon, actualizará la respuesta entonces
Biju jose
Gracias @Biju jose por un documento oficial.
Mariano G