Modifier l'index référencé pour une clé étrangère

9

J'ai quelque chose comme ça:

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

Pour des raisons de performances (et de blocage), j'ai créé un nouvel index sur T1

CREATE UNIQUE NONCLUSTERED INDEX IX_T1_Id ON T1 (Id)

Mais si je vérifie à quel index fait référence le FK, continue de faire référence à l'index clusterisé

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 je laisse tomber la contrainte et que je la crée à nouveau, elle fait référence à l'index non cluster, mais cela conduit à vérifier à nouveau tous les t2 FK.

Existe-t-il un moyen de changer cela pour que le FK_T2_T1 utilise IX_T1_Id au lieu de PK_T1 sans supprimer le FK et verrouiller la table lors de la vérification du FK?

Merci!

Mariano G
la source
Il y a eu une discussion pertinente ici .
i-one

Réponses:

6

Eh bien, après avoir continué la recherche, j'ai trouvé cet article

Contrairement à une requête normale, il ne récupérera pas un nouvel index en raison de la mise à jour des statistiques, de la création d'un nouvel index ou même du redémarrage d'un serveur. La seule façon dont je suis conscient d'avoir une liaison FK à un index différent est de supprimer et recréer le FK, le laissant sélectionner automatiquement l'index sans options pour le contrôler manuellement.

Sur quoi, à moins que quelqu'un ne puisse dire le contraire, je devrai chercher une fenêtre de temps pour effectuer cette tâche.

Merci

Mariano G
la source
2

Après avoir lu MS DOCS ici .

Pour modifier une clé étrangère

Pour modifier une contrainte FOREIGN KEY à l'aide de Transact-SQL, vous devez d'abord supprimer la contrainte FOREIGN KEY existante, puis la recréer avec la nouvelle définition. Pour plus d'informations, voir Supprimer des relations de clé étrangère et Créer des relations de clé étrangère.

Dans votre cas, je crois ajouter un nouveau FK et supprimer l'ancien. Pour désactiver l'analyse, vous pouvez utiliser l' NO CHECKoption

--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 
╚══════════╩════════════╩═════════════════╩══════════════╝

Voir si cela fonctionne, ce que j'essaie est d'ajouter un FK de plus pour que le nouveau soit lié au nouvel index créé et de supprimer l'ancien FK. Je sais que la question n'est pas de laisser tomber l'existant mais voyez si cette option vous aidera.

En outre, selon les commentaires de Max Vernon: "l'option WITH NOCHECK empêchera la clé étrangère d'être approuvée par l'optimiseur. À un moment donné, vous devrez modifier la clé étrangère afin qu'elle soit approuvée à l'aide de ALTER TABLE ... AVEC CHÈQUE "

Le NOCHECKne sera ignoré qu'au moment de la création, mais pour appliquer la contraint à l'intégrité, vous l'avez exécuté à un moment donné.

Biju jose
la source
l' WITH NOCHECKoption empêchera la clé étrangère d'être approuvée par l'optimiseur. À un moment donné, vous devrez modifier la clé étrangère afin qu'elle soit approuvée à l'aide deALTER TABLE ... WITH CHECK
Max Vernon
@MaxVernon donc cela signifie que nous n'avons pas d'option
Biju jose
correct. La seule façon d'obtenir la clé étrangère pour utiliser le nouvel index est de recréer la clé étrangère avec l'option CHECK intacte.
Max Vernon
@max Vernon, mettra alors à jour la réponse
Biju jose
Merci @Biju jose pour un document officiel.
Mariano G