Ändern Sie den referenzierten Index für einen Fremdschlüssel

9

Ich habe so etwas:

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

Aus Gründen der Leistung (und des Deadlocks) habe ich einen neuen Index für T1 erstellt

CREATE UNIQUE NONCLUSTERED INDEX IX_T1_Id ON T1 (Id)

Wenn ich jedoch überprüfe, auf welchen Index der FK verweist, wird weiterhin auf den Clustered-Index verwiesen

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

Wenn ich die Einschränkung lösche und erneut erstelle, verweist sie auf den nicht gruppierten Index. Dies führt jedoch dazu, dass alle t2 FK erneut überprüft werden.

Gibt es eine Möglichkeit, dies zu ändern, sodass der FK_T2_T1 IX_T1_Id anstelle von PK_T1 verwendet, ohne den FK zu löschen und die Tabelle bei der FK-Prüfung zu sperren?

Vielen Dank!

Mariano G.
quelle
Es gab eine relevante Diskussion hier .
i-one

Antworten:

6

Nachdem ich weiter gesucht hatte, fand ich diesen Artikel

Im Gegensatz zu einer normalen Abfrage wird kein neuer Index abgerufen, da Statistiken aktualisiert, ein neuer Index erstellt oder sogar ein Server neu gestartet wird. Die einzige Möglichkeit, eine FK-Bindung an einen anderen Index vorzunehmen, besteht darin, die FK zu löschen und neu zu erstellen, sodass sie den Index automatisch auswählt, ohne dass Optionen zur manuellen Steuerung vorhanden sind.

Daraufhin muss ich, sofern nicht jemand etwas anderes sagen kann, nach einem Zeitfenster suchen, um diese Aufgabe auszuführen.

Vielen Dank

Mariano G.
quelle
2

Nach dem Lesen von MS DOCS hier .

So ändern Sie einen Fremdschlüssel

Um eine FOREIGN KEY-Einschränkung mithilfe von Transact-SQL zu ändern, müssen Sie zuerst die vorhandene FOREIGN KEY-Einschränkung löschen und sie dann mit der neuen Definition neu erstellen. Weitere Informationen finden Sie unter Löschen von Fremdschlüsselbeziehungen und Erstellen von Fremdschlüsselbeziehungen.

In Ihrem Fall, glaube ich, fügen Sie einen neuen FK hinzu und löschen Sie den alten. Um den Scan zu deaktivieren, können Sie die NO CHECKOption verwenden

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

Wenn dies funktioniert, versuche ich, einen weiteren FK hinzuzufügen, damit der neue mit dem neu erstellten Index verknüpft wird, und den alten FK zu löschen. Ich weiß, dass die Frage nicht darin besteht, die vorhandene zu löschen, sondern zu prüfen, ob diese Option Ihnen hilft.

Gemäß den Kommentaren von Max Vernon: "Die Option WITH NOCHECK verhindert, dass der Optimierer dem Fremdschlüssel vertraut. Irgendwann müssten Sie den Fremdschlüssel so ändern, dass er mit ALTER TABLE vertrauenswürdig ist ... MIT PRÜFUNG "

Das NOCHECKwird nur zum Zeitpunkt der Erstellung ignoriert, aber um die Integrität zu verhindern, haben Sie dies zu einem bestimmten Zeitpunkt ausgeführt.

Biju Jose
quelle
Die WITH NOCHECKOption verhindert, dass der Optimierer dem Fremdschlüssel vertraut. Irgendwann müssten Sie den Fremdschlüssel so ändern, dass er vertrauenswürdig istALTER TABLE ... WITH CHECK
Max Vernon
@ MaxVernon, das heißt, wir haben keine Option
Biju Jose
richtig. Die einzige Möglichkeit, den Fremdschlüssel zur Verwendung des neuen Index zu bewegen, besteht darin, den Fremdschlüssel mit intakter Option CHECK neu zu erstellen.
Max Vernon
@ Max Vernon, wird die Antwort dann aktualisieren
Biju Jose
Vielen Dank an @Biju Jose für ein offizielles Dokument.
Mariano G