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!


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


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.