Alterar índice referenciado para uma chave estrangeira

9

Eu tenho algo parecido com isto:

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 motivos de desempenho (e deadlock), criei um novo índice no T1

CREATE UNIQUE NONCLUSTERED INDEX IX_T1_Id ON T1 (Id)

Mas se eu verificar para qual índice faz referência ao FK, continuarei fazendo referência ao índice em cluster

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

Se eu largar a restrição e criar novamente, ela fará referência ao índice não clusterizado, mas isso levará a verificar todos os T2 FK novamente.

Existe uma maneira de mudar isso para que o FK_T2_T1 use IX_T1_Id em vez de PK_T1 sem soltar o FK e bloquear a tabela na verificação do FK?

Obrigado!

Mariano G
fonte
Houve uma discussão relevante aqui .
i-ona

Respostas:

6

Bem, depois de continuar pesquisando, encontrei este artigo

Diferentemente de uma consulta normal, ele não seleciona um novo índice devido à atualização das estatísticas, à criação de um novo índice ou à reinicialização de um servidor. A única maneira de saber que um FK é vinculado a um índice diferente é soltar e recriar o FK, permitindo que ele selecione automaticamente o índice sem opções para controlá-lo manualmente.

Portanto, a menos que alguém possa dizer o contrário, terei que procurar uma janela de tempo para executar esta tarefa.

obrigado

Mariano G
fonte
2

Depois de ler o MS DOCS aqui .

Para modificar uma chave estrangeira

Para modificar uma restrição FOREIGN KEY usando o Transact-SQL, você deve primeiro excluir a restrição FOREIGN KEY existente e depois recriá-la com a nova definição. Para obter mais informações, consulte Excluir relacionamentos de chave estrangeira e Criar relacionamentos de chave estrangeira.

No seu caso, acredito que adicione um novo FK e exclua o antigo. Para desativar a verificação, você pode usar a NO CHECKopção

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

Veja se isso funciona, o que estou tentando é adicionar mais um FK para que o novo esteja vinculado ao novo índice criado e solte o antigo FK. Eu sei que a pergunta é não largar a existente, mas ver se esta opção irá ajudá-lo.

Além disso, conforme os comentários de Max Vernon: "a opção WITH NOCHECK impedirá que a chave estrangeira seja confiável pelo otimizador. Em algum momento, você precisará alterar a chave estrangeira para que seja confiável usando ALTER TABLE ... COM VERIFICAÇÃO "

A NOCHECKúnica será ignorado no momento da criação, mas para impor a integridade contraint você executar este em algum ponto do tempo.

Biju jose
fonte
a WITH NOCHECKopção impedirá que a chave estrangeira seja confiável pelo otimizador. Em algum momento, você teria que alterar a chave estrangeira para que ela seja confiável usando o #ALTER TABLE ... WITH CHECK
Max Vernon
@MaxVernon Então isso significa que não temos uma opção
Biju jose
corrigir. A única maneira de obter a chave estrangeira para usar o novo índice é recriar a chave estrangeira com a opção CHECK intacta.
Max Vernon
@max Vernon, irá atualizar a resposta, então
Biju jose
Obrigado @Biju jose por um documento oficial.
Mariano G