Co zostanie utracone, gdy utworzę klucz obcy za pomocą `WITH NOCHECK`?

11

Wiem, że jeśli wykonam EXISTS()wywołanie wartości wyszukiwania FK, to jeśli to ograniczenie FK jest zaufane, wynik jest natychmiastowy.

A jeśli nie jest zaufany (np. Kiedy tworzę FK za pomocą WITH NOCHECK), SQL Server musi przejść i sprawdzić tabelę, aby sprawdzić, czy wartość rzeczywiście tam jest.

Czy mogę coś jeszcze stracić NOCHECK?

Vaccano
źródło

Odpowiedzi:

13

Jak odkryłeś na existsprzykładzie, SQL Server może wykorzystać fakt, że klucz obcy jest zaufany podczas budowania planu zapytań.

Czy mogę coś jeszcze stracić, używając NOCHECK?

Oprócz tego, że możesz dodać wartości do kolumny, których nie powinno tam być, na co odpowiada Ste Bov , będziesz miał więcej scenariuszy, w których plan zapytań będzie lepszy, gdy zaufany klucz obcy.

Oto jeden przykład z widokiem indeksowanym .

Masz dwie tabele z zaufanym ograniczeniem FK.

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

Nie ma tak wielu krajów, ale gazillion miast, a niektóre z nich to duże miasta.

Przykładowe dane:

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

Najczęściej wykonywane zapytania w tej aplikacji dotyczą znalezienia liczby dużych miast na kraj. Aby to przyspieszyć, dodajemy widok indeksowany.

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

Po chwili pojawia się potrzeba dodania nowego kraju

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

Plan zapytań dla tej wstawki nie ma niespodzianek.

wprowadź opis zdjęcia tutaj

Klastrowany indeks wstawia do Countrytabeli.

Teraz, jeśli twój klucz obcy nie był zaufany

alter table dbo.City nocheck constraint FK_CountryID;

i dodajesz nowy kraj

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

skończyłbyś z tym niezbyt ładnym obrazem.

wprowadź opis zdjęcia tutaj

Dolna gałąź służy do aktualizacji widoku indeksowanego. Wykonuje pełny skan tabeli, Cityaby dowiedzieć się, czy kraj, w którym są CountryID = 5już wiersze w tabeli City.

Gdy klucz jest zaufany, SQL Server wie, że nie może być żadnych wierszy City, które pasowałyby do nowego wiersza Country.

Mikael Eriksson
źródło
4

Tracisz optymalizację zapytań. W praktyce jedyną optymalizacją, którą pamiętam, jest eliminacja zbędnych połączeń. Na przykład, jeśli masz widok:

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

A gdy korzystasz z widoku, nie korzystasz z kolumn ctego połączenia, które można usunąć, jeśli istnieje prawidłowe ustawienie FK.

Twój EXISTSprzykład to szczególny przypadek usunięcia zbędnego łączenia. Nie sądzę, aby ten konkretny przykład był praktycznie istotny.

Tracisz także ścisłą integralność danych zapewnianą przez zaufane ograniczenie.

usr
źródło
3

Opcja NOCHECK robi dokładnie to, co mówi na puszce.

Służy głównie do dodawania klucza obcego w połowie istnienia tabeli, w której istnieje nowa relacja, która mogła nie być wymagana (przynajmniej tak rozumiem).

Oznacza to, że kolumna zawierająca klucz obcy MOŻE mieć w sobie wartości, które nie korelują z wyznaczoną wartością, do której powinna się odnosić.

Oznacza to, że gdy masz opcję NOCHECK na SQL Server, musisz faktycznie przejść i sprawdzić, czy ta wartość klucza jest rzeczywiście kluczem podstawowym. Jeśli NOCHECK nie jest ustawiony, SQL Server zakłada, że ​​wszystko, co jest w tej kolumnie, zdecydowanie istnieje, ponieważ wpis nie mógłby istnieć w tabeli, jeśli nie byłby to już klucz podstawowy, i nie można usunąć klucza podstawowego bez usuwania wiersza w pytanie.

Po prostu NOCHECK jest kluczem obcym, któremu nie można ufać, jeśli chodzi o cokolwiek.

Tak naprawdę nie tracisz nic poza zaufaniem, jakie gwarantowałby klucz podstawowy.

Ste Bov
źródło
Z odpowiedzi nie wynika jasno, czy istnieje wymuszenie klucza obcego po początkowym utworzeniu ograniczenia. Czy możesz wyjaśnić, co się stanie, jeśli spróbujesz INSERTnowego wiersza, który odnosi się do nieistniejącego wiersza nadrzędnego lub jeśli spróbujesz DELETEwiersza, który ma wiersze podrzędne później?
jpmc26