Mất gì khi tôi tạo Khóa ngoại bằng cách sử dụng `VỚI NOCHECK`?


11

Tôi biết rằng nếu tôi thực hiện một EXISTS()cuộc gọi trên giá trị tra cứu FK, thì, nếu ràng buộc FK đó được tin cậy, kết quả là ngay lập tức.

Và nếu nó không đáng tin cậy (như khi tôi tạo FK bằng cách sử dụng WITH NOCHECK) thì SQL Server phải đi và kiểm tra bảng để xem giá trị có thực sự ở đó không.

Có bất cứ điều gì khác tôi mất bằng cách sử dụng NOCHECK?

Câu trả lời:


13

Như bạn đã khám phá với existsví dụ của mình , SQL Server có thể sử dụng thực tế là khóa ngoại được tin cậy khi kế hoạch truy vấn được xây dựng.

Có điều gì khác tôi mất bằng cách sử dụng NOCHECK không?

Ngoài việc bạn có thể thêm các giá trị vào một cột không có ở đó như được trả lời bởi Ste Bov, bạn sẽ có nhiều kịch bản hơn trong đó kế hoạch truy vấn sẽ tốt hơn khi khóa ngoại được tin cậy.

Đây là một ví dụ với chế độ xem được lập chỉ mục .

Bạn có hai bảng với ràng buộc FK đáng tin cậy.

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

Không có nhiều quốc gia nhưng có rất nhiều thành phố và một số trong số đó là những thành phố lớn.

Dữ liệu mẫu:

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

Các truy vấn được thực hiện thường xuyên nhất trong ứng dụng này có liên quan đến việc tìm kiếm số lượng thành phố lớn trên mỗi quốc gia. Để tăng tốc mọi thứ với điều đó, chúng tôi thêm một chế độ xem được lập chỉ mục.

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

Sau một thời gian, nhu cầu thêm một quốc gia mới

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

Kế hoạch truy vấn cho chèn đó không có gì ngạc nhiên.

nhập mô tả hình ảnh ở đây

Một chỉ mục cụm chèn vào Countrybảng.

Bây giờ, nếu khóa ngoại của bạn không được tin cậy

alter table dbo.City nocheck constraint FK_CountryID;

và bạn thêm một quốc gia mới

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

bạn sẽ kết thúc với hình ảnh không đẹp này.

nhập mô tả hình ảnh ở đây

Chi nhánh thấp hơn ở đó để cập nhật chế độ xem được lập chỉ mục. Nó thực hiện quét toàn bộ bảng Cityđể tìm hiểu xem quốc gia CountryID = 5đã có hàng trong bảng chưa City.

Khi khóa được tin cậy, SQL Server biết rằng không thể có hàng Citynào khớp với hàng mới trong Country.


4

Bạn đang mất tối ưu hóa truy vấn. Trong thực tế, tối ưu hóa duy nhất mà tôi nhớ lại là loại bỏ các phép nối dự phòng. Ví dụ: nếu bạn có chế độ xem:

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

Và khi sử dụng chế độ xem, bạn không sử dụng các cột của cphép nối đó có thể bị xóa nếu có một FK thích hợp được thiết lập.

EXISTSVí dụ của bạn là một trường hợp đặc biệt để loại bỏ một liên kết dự phòng. Tôi không nghĩ rằng ví dụ cụ thể đó thực tế có liên quan.

Bạn cũng mất tính toàn vẹn dữ liệu nghiêm ngặt mà một ràng buộc đáng tin cậy cung cấp.


3

Tùy chọn NOCHECK thực hiện chính xác những gì nó nói trên hộp thiếc.

Nó chủ yếu được sử dụng để thêm khóa ngoại trên một nửa trong suốt sự tồn tại của một bảng trong đó có một mối quan hệ mới có thể không được yêu cầu (ít nhất đó là sự hiểu biết của tôi).

Điều đó có nghĩa là cột có khóa ngoại CÓ THỂ có các giá trị bên trong nó không tương quan với giá trị được chỉ định mà nó nên liên quan.

Điều này có nghĩa là khi bạn có tùy chọn NOCHECK trên SQL Server phải thực sự đi và kiểm tra xem giá trị khóa đó có thực sự là khóa chính hay không. Nếu NOCHECK không được đặt thì SQL Server cho rằng mọi thứ trong cột đó chắc chắn tồn tại vì mục nhập không thể tồn tại trong bảng nếu nó chưa phải là khóa chính và bạn không thể xóa khóa chính mà không xóa hàng trong câu hỏi

Đơn giản là NOCHECK là một khóa ngoại mà bạn không thể tin tưởng để thực sự liên quan đến bất cứ điều gì.

Bạn không thực sự mất bất cứ thứ gì ngoài sự tin tưởng rằng khóa chính được đảm bảo ở đó.


Không rõ câu trả lời của bạn cho dù có bất kỳ sự thực thi nào của khóa ngoại sau khi tạo ràng buộc ban đầu . Bạn có thể làm rõ điều gì xảy ra nếu bạn thử INSERTmột hàng mới liên quan đến một hàng cha mẹ không tồn tại hoặc nếu bạn cố gắng DELETEmột hàng có các hàng con sau này không?
jpmc26
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.