O que é perdido quando eu crio uma chave estrangeira usando `WITH NOCHECK`?

11

Sei que, se eu fizer uma EXISTS()chamada em um valor de pesquisa de FK, se essa restrição de FK for confiável, o resultado será imediato.

E se não for confiável (como quando eu crio o FK usando WITH NOCHECK), o SQL Server precisará ir e verificar a tabela para ver se o valor está realmente lá.

Há mais alguma coisa que eu perco usando NOCHECK?

Vaccano
fonte

Respostas:

13

Como você descobriu com seu existsexemplo, o SQL Server pode usar o fato de que uma chave estrangeira é confiável quando o plano de consulta é criado.

Há mais alguma coisa que eu perco usando o NOCHECK?

Além do fato de você poder adicionar valores a uma coluna que não deveria estar lá, conforme respondido por Ste Bov, você terá mais cenários em que o plano de consulta será melhor quando a chave estrangeira for confiável.

Aqui está um exemplo com uma exibição indexada .

Você tem duas tabelas com uma restrição FK confiável.

create table dbo.Country
(
  CountryID int primary key,
  Name varchar(50) not null
);

create table dbo.City
(
  CityID int identity primary key,
  Name varchar(50),
  IsBig bit not null,
  CountryID int not null
);

alter table dbo.City 
  add constraint FK_CountryID 
  foreign key (CountryID) 
  references dbo.Country(CountryID);

Não existem muitos países, mas um gazilhão de cidades e algumas delas são grandes cidades.

Dados de amostra:

-- Three countries
insert into dbo.Country(CountryID, Name) values
(1, 'Sweden'),
(2, 'Norway'),
(3, 'Denmark');

-- Five big cities
insert into dbo.City(Name, IsBig, CountryID) values
('Stockholm', 1, 1),
('Gothenburg', 1, 1),
('Malmoe', 1, 1),
('Oslo', 1, 2),
('Copenhagen', 1, 3);

-- 300 small cities
insert into dbo.City(Name, IsBig, CountryID)
select 'NoName', 0, Country.CountryID
from dbo.Country
  cross apply (
              select top(100) *
              from sys.columns
              ) as T;

As consultas mais frequentemente executadas neste aplicativo estão relacionadas à localização do número de grandes cidades por país. Para acelerar as coisas, adicionamos uma exibição indexada.

create view dbo.BigCityCount with schemabinding
as
select count_big(*) as BigCityCount,
       City.CountryID,
       Country.Name as CountryName
from dbo.City
  inner join dbo.Country
    on City.CountryID = Country.CountryID
where City.IsBig = 1 
group by City.CountryID,
         Country.Name;

 go

create unique clustered index CX_BigCityCount
  on dbo.BigCityCount(CountryID);

Depois de um tempo, vem a demanda de adicionar um novo país

insert into dbo.Country(CountryID, Name) values(4, 'Finland');

O plano de consulta para essa inserção não apresenta surpresas.

insira a descrição da imagem aqui

Uma inserção de índice em cluster na Countrytabela.

Agora, se sua chave estrangeira não era confiável

alter table dbo.City nocheck constraint FK_CountryID;

e você adiciona um novo país

insert into dbo.Country(CountryID, Name) values(5, 'Iceland');

você acabaria com essa imagem não tão bonita.

insira a descrição da imagem aqui

A ramificação inferior existe para atualizar a exibição indexada. Ele faz uma varredura completa da tabela Citypara descobrir se o país CountryID = 5já possui linhas na tabela City.

Quando a chave é confiável, o SQL Server sabe que não pode haver linhas Cityque correspondam à nova linha Country.

Mikael Eriksson
fonte
4

Você está perdendo otimizações de consulta. Na prática, a única otimização que me lembro é a eliminação de junções redundantes. Por exemplo, se você tem uma visão:

select *
from Orders o
join Customers c on o.CustomerID = c.ID

E, ao usar a visualização, você não utiliza as colunas da cjunção que pode ser excluída se houver um FK configurado corretamente.

Seu EXISTSexemplo é um caso especial de remoção de uma junção redundante. Não acho que esse exemplo específico seja praticamente relevante.

Você também perde a estrita integridade de dados fornecida por uma restrição confiável.

usr
fonte
3

A opção NOCHECK faz exatamente o que diz na lata.

É usado principalmente para adicionar uma chave estrangeira no meio da existência de uma tabela em que há um novo relacionamento que pode não ter sido necessário (pelo menos é o meu entendimento).

Isso significa que a coluna que possui a chave estrangeira PODE ter valores dentro dela que não se correlacionam com o valor designado ao qual ela deve se relacionar.

Isso significa que, quando você tem uma opção NOCHECK no SQL Server, precisa verificar se esse valor da chave é realmente uma chave primária. Se NOCHECK não estiver definido, o SQL Server presume que tudo o que está nessa coluna definitivamente existe porque a entrada não poderia existir na tabela se ela ainda não fosse uma chave primária e você não poderia excluir a chave primária sem excluir a linha em questão.

Simplesmente NOCHECK é uma chave estrangeira na qual você não pode confiar para se relacionar com algo.

Na verdade, você não está perdendo nada além da confiança que a chave primária garantiu estar lá.

Ste Bov
fonte
Não está claro em sua resposta se existe alguma imposição da chave estrangeira após a criação inicial da restrição. Você poderia esclarecer o que acontece se você tentar INSERTuma nova linha relacionada a uma linha pai inexistente ou se você tentar DELETEuma linha que tenha linhas filho posteriormente?
precisa saber é o seguinte