web-dev-qa-db-ja.com

`WITH NOCHECK`を使用して外部キーを作成すると、何が失われますか?

FKルックアップ値に対してEXISTS()呼び出しを行うと、そのFK制約が信頼できる場合、結果はすぐにわかります。

そして、それが信頼されていない場合(WITH NOCHECKを使用してFKを作成するときなど)、SQL Serverはテーブルに移動して、値が実際に存在するかどうかを確認する必要があります。

NOCHECKを使用して失うものは他にありますか?

11
Vaccano

existsの例でわかったように、SQL Serverは、クエリプランの構築時に外部キーが信頼されているという事実を使用できます。

NOCHECKを使用することで他に失うものはありますか?

Ste Bov で回答されているはずのない列に値を追加できるという事実とは別に、外部キーが信頼されている場合にクエリプランがより適切になるシナリオが増えます。

インデックス付きビュー を使用した1つの例を次に示します。

信頼できるFK制約を持つ2つのテーブルがあります。

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

その挿入のクエリプランに驚くことはありません。

enter image description here

Countryテーブルへのクラスター化インデックスの挿入。

さて、あなたの外部キーが信頼されていなかった場合

alter table dbo.City nocheck constraint FK_CountryID;

新しい国を追加します

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

あなたはこれでそれほどきれいではない絵になるでしょう。

enter image description here

下のブランチは、インデックス付きビューを更新するためにあります。 Cityの全テーブルスキャンを実行して、国がCountryID = 5にはすでにテーブルCityに行があります。

キーが信頼されると、SQL Serverは、Cityの新しい行と一致するCountryの行がないことを認識します。

13
Mikael Eriksson

クエリの最適化を失っています。実際、私が思い出す唯一の最適化は、冗長な結合の排除です。たとえば、ビューにいる場合:

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

また、ビューを使用する場合、cの列を使用しないと、適切なFKが設定されていれば、その結合を削除できます。

EXISTSの例は、冗長結合を削除する特別な場合です。特定の例が実際に関連しているとは思いません。

また、信頼できる制約が提供する厳密なデータ整合性も失われます。

4
usr

NOCHECKオプションは、缶に書かれていることを正確に実行します。

これは主に、必要とされていなかった可能性のある新しい関係があるテーブルの存在の途中で外部キーを追加するために使用されます(少なくとも私の理解です)。

これは、外部キーを持つ列に、関連する指定された値と相関しない値が含まれる可能性があることを意味します。

つまり、SQL ServerにNOCHECKオプションがある場合、実際に行って、そのキー値が実際に主キーであるかどうかを確認する必要があります。 NOCHECKが設定されていない場合、SQL Serverは、その列にあるものはすべて存在するものと見なします。これは、主キーでなかった場合、エントリがテーブルに存在せず、行を削除せずに主キーを削除できなかったためです。質問。

単にNOCHECKは、実際に何かに関係するとは信頼できない外部キーです。

実際には、主キーが存在することが保証されているという信頼以外は何も失っていません。

3
Ste Bov