Изменить ссылочный индекс для внешнего ключа

9

У меня есть что-то вроде этого:

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

По причинам производительности (и тупика) я создал новый индекс для T1

CREATE UNIQUE NONCLUSTERED INDEX IX_T1_Id ON T1 (Id)

Но если я проверяю, какой индекс ссылается на FK, продолжает ссылаться на кластерный индекс

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

Если я отбрасываю ограничение и создаю заново, он ссылается на некластеризованный индекс, но это приводит к повторной проверке всех t2 FK.

Есть ли способ изменить это так, чтобы FK_T2_T1 использовал IX_T1_Id вместо PK_T1, не удаляя FK и не блокируя таблицу на проверку FK?

Спасибо!

Мариано Г
источник
Было соответствующее обсуждение здесь .
я один

Ответы:

6

Ну а после продолжения поиска я нашел эту статью

В отличие от обычного запроса, он не получит новый индекс из-за обновления статистики, создания нового индекса или даже перезагрузки сервера. Единственный способ связать FK с другим индексом - это удалить и воссоздать FK, позволяя ему автоматически выбирать индекс без каких-либо опций для управления им вручную.

После чего, если кто-то не скажет иначе, мне придется искать временное окно для выполнения этой задачи.

Спасибо

Мариано Г
источник
2

После прочтения MS DOCS здесь .

Чтобы изменить внешний ключ

Чтобы изменить ограничение FOREIGN KEY с помощью Transact-SQL, сначала необходимо удалить существующее ограничение FOREIGN KEY, а затем заново создать его с новым определением. Для получения дополнительной информации см. Удаление отношений внешнего ключа и Создание отношений внешнего ключа.

В вашем случае я считаю, добавьте новый FK и удалите старый. Для отключения сканирования вы можете использовать NO CHECKопцию

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

Посмотрите, работает ли это, я пытаюсь добавить еще один FK, чтобы новый связывался с созданным новым индексом, и отбрасывать старый FK. Я знаю, что вопрос не в том, чтобы отбросить существующий, а посмотреть, поможет ли этот вариант.

Кроме того, согласно комментариям Макса Вернона: «Опция WITH NOCHECK предотвратит доверие внешнего ключа к оптимизатору. В какой-то момент вам придется изменить внешний ключ так, чтобы ему доверяли, используя ALTER TABLE ... С ПРОВЕРКОЙ "

NOCHECKБудут игнорироваться только в момент создания , но для обеспечения целостности contraint вы запускали это в какой - то момент времени.

Бижу Хосе
источник
эта WITH NOCHECKопция предотвратит доверие внешнего ключа к оптимизатору. В какой-то момент вам придется изменить внешний ключ так, чтобы ему доверяли с помощьюALTER TABLE ... WITH CHECK
Макс Вернон
@MaxVernon, так что это означает, что у нас нет выбора
Биджу
правильный. Единственный способ получить внешний ключ для использования нового индекса - это воссоздать внешний ключ с неизменной опцией CHECK.
Макс Вернон,
@max Вернон, обновит ответ тогда
Biju jose
Спасибо @Biju Jose за официальный документ.
Мариано G