Cosa si perde quando creo una chiave esterna usando `WITH NOCHECK`?


11

So che se faccio una EXISTS()chiamata su un valore di ricerca FK, quindi, se quel vincolo FK è attendibile, il risultato è immediato.

E se non è attendibile (come quando creo l'FK usando WITH NOCHECK), SQL Server deve andare e controllare la tabella per vedere se il valore è effettivamente lì.

C'è qualcos'altro che perdo usando NOCHECK?

Risposte:


13

Come hai scoperto con il tuo existsesempio, SQL Server può utilizzare il fatto che una chiave esterna è attendibile quando viene creato il piano di query.

C'è qualcos'altro che perdo usando NOCHECK?

A parte il fatto che è possibile aggiungere valori a una colonna che non dovrebbe essere lì come ha risposto Ste Bov, si avranno più scenari in cui il piano di query sarà migliore quando la chiave esterna è attendibile.

Ecco un esempio con una vista indicizzata .

Hai due tabelle con un vincolo FK attendibile.

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

Non ci sono così tanti paesi, ma un gazillion di città e alcuni di loro sono grandi città.

Dati di esempio:

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

Le query eseguite più spesso in questa applicazione sono legate alla ricerca del numero di grandi città per paese. Per accelerare le cose con ciò aggiungiamo una vista indicizzata.

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

Dopo un po 'arriva la richiesta di aggiungere un nuovo paese

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

Il piano di query per quell'inserto non presenta sorprese.

inserisci qui la descrizione dell'immagine

Un indice cluster inserito nella Countrytabella.

Ora, se la tua chiave esterna non era attendibile

alter table dbo.City nocheck constraint FK_CountryID;

e aggiungi un nuovo paese

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

finiresti con questa foto non così bella.

inserisci qui la descrizione dell'immagine

Il ramo inferiore è lì per aggiornare la vista indicizzata. Esegue una scansione completa della tabella Cityper capire se il paese con CountryID = 5ha già delle righe nella tabella City.

Quando la chiave è attendibile, SQL Server sa che non ci possono essere righe Cityche corrispondano alla nuova riga in Country.


4

Stai perdendo le ottimizzazioni delle query. In pratica, l'unica ottimizzazione che ricordo è l'eliminazione dei join ridondanti. Ad esempio se hai in una vista:

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

E quando usi la vista non usi le colonne di cquel join che può essere cancellato se c'è un FK corretto impostato.

Il tuo EXISTSesempio è un caso speciale di rimozione di un join ridondante. Non credo che quel particolare esempio sia praticamente rilevante.

Si perde anche la rigorosa integrità dei dati fornita da un vincolo di fiducia.


3

L'opzione NOCHECK fa esattamente quello che dice sulla scatola.

Viene utilizzato principalmente per aggiungere una chiave esterna a metà dell'esistenza di una tabella in cui esiste una nuova relazione che potrebbe non essere stata richiesta (almeno questa è la mia comprensione).

Significa che la colonna che ha la chiave esterna può avere valori al suo interno che non sono correlati al valore designato a cui dovrebbe fare riferimento.

Ciò significa che quando si dispone di un'opzione NOCHECK su SQL Server deve effettivamente andare e verificare se quel valore di chiave è effettivamente una chiave primaria. Se NOCHECK non è impostato, SQL Server presume che tutto ciò che si trova in quella colonna esiste sicuramente perché la voce non potrebbe esistere nella tabella se non fosse già una chiave primaria e non è possibile eliminare la chiave primaria senza eliminare la riga in domanda.

Semplicemente NOCHECK è una chiave esterna di cui non ci si può fidare di cui effettivamente si relaziona a qualcosa.

In realtà non stai perdendo altro che la fiducia che la chiave primaria garantiva di essere lì.


Dalla tua risposta non è chiaro se sia stata applicata la chiave esterna dopo la creazione iniziale del vincolo. Potresti chiarire cosa succede se provi a INSERTuna nuova riga correlata a una riga padre inesistente o se provi in DELETEseguito una riga con righe figlio?
jpmc26,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.