Qu'est-ce qui est perdu lorsque je crée une clé étrangère en utilisant `WITH NOCHECK`?

11

Je sais que si je fais un EXISTS()appel sur une valeur de recherche FK, alors, si cette contrainte FK est approuvée, le résultat est immédiat.

Et s'il n'est pas approuvé (comme lorsque je crée le FK à l'aide WITH NOCHECK), SQL Server doit aller vérifier dans le tableau pour voir si la valeur est réellement là.

Y a-t-il autre chose que je perds en utilisant NOCHECK?

Vaccano
la source

Réponses:

13

Comme vous l'avez découvert avec votre existsexemple, SQL Server peut utiliser le fait qu'une clé étrangère est approuvée lors de la génération du plan de requête.

Y a-t-il autre chose que je perds en utilisant NOCHECK?

Mis à part le fait que vous pouvez ajouter des valeurs à une colonne qui ne devrait pas être là comme l'a répondu Ste Bov, vous aurez plus de scénarios où le plan de requête sera meilleur lorsque la clé étrangère est approuvée.

Voici un exemple avec une vue indexée .

Vous disposez de deux tables avec une contrainte FK approuvée.

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

Il n'y a pas tant de pays que des millions de villes et certains d'entre eux sont de grandes villes.

Exemples de données:

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

Les requêtes les plus souvent exécutées dans cette application sont liées à la recherche du nombre de grandes villes par pays. Pour accélérer les choses, nous ajoutons une vue indexée.

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

Après un certain temps vient la demande d'ajouter un nouveau pays

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

Le plan de requête pour cet insert n'a aucune surprise.

entrez la description de l'image ici

Une insertion d'index cluster dans la Countrytable.

Maintenant, si votre clé étrangère n'était pas fiable

alter table dbo.City nocheck constraint FK_CountryID;

et vous ajoutez un nouveau pays

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

vous vous retrouveriez avec cette image pas si jolie.

entrez la description de l'image ici

La branche inférieure est là pour mettre à jour la vue indexée. Il effectue une analyse complète du tableau Citypour déterminer si le pays avec CountryID = 5possède déjà des lignes dans le tableau City.

Lorsque la clé est approuvée, SQL Server sait qu'aucune ligne ne Citypeut correspondre à la nouvelle ligne Country.

Mikael Eriksson
la source
4

Vous perdez les optimisations de requête. En pratique, la seule optimisation dont je me souvienne est l'élimination des jointures redondantes. Par exemple, si vous avez dans une vue:

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

Et lorsque vous utilisez la vue, vous n'utilisez pas les colonnes de ccette jointure qui peut être supprimée s'il existe une configuration FK appropriée.

Votre EXISTSexemple est un cas particulier de suppression d'une jointure redondante. Je ne pense pas que cet exemple particulier soit pratiquement pertinent.

Vous perdez également l'intégrité stricte des données fournie par une contrainte de confiance.

usr
la source
3

L'option NOCHECK fait exactement ce qu'elle dit sur l'étain.

Il est principalement utilisé pour ajouter une clé étrangère à mi-chemin de l'existence d'une table où il y a une nouvelle relation qui n'a peut-être pas été requise (c'est ce que je comprends du moins).

Cela signifie que la colonne qui a la clé étrangère PEUT contenir des valeurs qui ne correspondent pas à la valeur désignée à laquelle elle doit se rapporter.

Cela signifie que lorsque vous avez une option NOCHECK sur SQL Server, vous devez réellement vérifier si cette valeur de clé est réellement une clé primaire. Si NOCHECK n'est pas défini, SQL Server suppose que tout ce qui se trouve dans cette colonne existe définitivement car l'entrée ne pourrait pas exister dans la table si elle n'était pas déjà une clé primaire et vous ne pouviez pas supprimer la clé primaire sans supprimer la ligne dans question.

Simplement NOCHECK est une clé étrangère à laquelle vous ne pouvez pas faire confiance pour réellement vous relier à quoi que ce soit.

Vous ne perdez rien d'autre que la confiance que la clé primaire garantit d'être là.

Ste Bov
la source
Il ne ressort pas clairement de votre réponse s'il existe une application de la clé étrangère après la création initiale de la contrainte. Pourriez-vous clarifier ce qui se passe si vous essayez INSERTune nouvelle ligne qui se rapporte à une ligne parent inexistante ou si vous essayez DELETEplus tard une ligne qui a des lignes enfants?
jpmc26