Що втрачається, коли я створюю зовнішній ключ, використовуючи "З НОЧЕК"?


11

Я знаю, що якщо я виконую EXISTS()дзвінок за значенням пошуку ФК, тоді, якщо цьому обмеженню ФК довіряти, результат негайний.

І якщо йому не довіряти (як, наприклад, коли я створюю FK за допомогою WITH NOCHECK), тоді SQL Server повинен зайти і перевірити таблицю, щоб побачити, чи є значення насправді.

Чи є ще щось, що я втрачаю, використовуючи NOCHECK?

Відповіді:


13

Як ви виявили на своєму existsприкладі, SQL Server може використовувати той факт, що іноземному ключу довіряють, коли будується план запитів.

Чи є ще щось, що я втрачаю, використовуючи NOCHECK?

Крім того, що ви можете додати значення до стовпця, який не повинен бути там, на що відповів Ste Bov, у вас буде більше сценаріїв, коли план запитів буде кращим, коли іноземному ключу довіряти.

Ось один приклад з індексованим видом .

У вас є дві таблиці з обмеженим обмеженням 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);

Є не так багато країн, але мільйон міст, а деякі з них - великі міста.

Приклад даних:

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

Найчастіше виконуються запити в цій програмі пов’язані з пошуку кількості великих міст в країні. Щоб прискорити цю ситуацію, ми додамо індексований вигляд.

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

Через деякий час з'являється запит на додавання нової країни

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

План запитів для цієї вставки не має сюрпризів.

введіть тут опис зображення

Кластеризована вставка індексу до Countryтаблиці.

Тепер, якщо ваш зовнішній ключ не довіряли

alter table dbo.City nocheck constraint FK_CountryID;

і ви додаєте нову країну

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

ви б закінчилися з цією не дуже гарною картиною.

введіть тут опис зображення

Нижня гілка є там, щоб оновити індексований вигляд. Він виконує повне сканування таблиці, Cityщоб визначити, чи є країна з CountryID = 5рядками в таблиці City.

Коли ключ довіряється, SQL Server знає, що у ньому не може бути рядків, Cityякі б відповідали новому рядку в Country.


4

Ви втрачаєте оптимізацію запитів. На практиці єдиною оптимізацією, яку я пам'ятаю, є усунення зайвих з'єднань. Наприклад, якщо у вас є вид:

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

І при використанні представлення ви не використовуєте стовпці, які cпотім приєднуються, можна видалити, якщо є належна настройка FK.

Ваш EXISTSприклад - це особливий випадок видалення зайвого з'єднання. Я не думаю, що конкретний приклад практично актуальний.

Ви також втрачаєте сувору цілісність даних, яку забезпечує довірене обмеження.


3

Параметр NOCHECK робить саме те, що написано на бляшанці.

В основному він використовується для додавання зовнішнього ключа наполовину через існування таблиці, де є нове співвідношення, яке, можливо, не потрібно (це, принаймні, моє розуміння).

Це означає, що стовпець із зовнішнім ключем МОЖЕ мати в ньому значення, які не співвідносяться із призначеним значенням, яке воно повинно стосуватися.

Це означає, що коли у вас є параметр NOCHECK на SQL Server, він повинен фактично перейти і перевірити, чи є значення цього ключа фактично первинним ключем. Якщо NOCHECK не встановлений, SQL Server припускає, що все, що знаходиться в цьому стовпці, безумовно, існує, тому що запис не міг існувати в таблиці, якщо це вже не був первинний ключ, і ви не змогли видалити первинний ключ, не видаляючи рядок у питання.

Просто NOCHECK - це зовнішній ключ, якому ви не можете довіряти, що насправді стосується нічого.

Ви насправді не втрачаєте нічого, окрім довіри, яку первинний ключ гарантував там.


З вашої відповіді незрозуміло, чи існує виконання закордонного ключа після первинного створення обмеження. Чи можете ви пояснити, що відбувається, якщо ви спробуєте INSERTновий рядок, який стосується неіснуючого батьківського рядка, або якщо ви спробуєте DELETEпізніше рядок, який має дочірні рядки?
jpmc26
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.