Apa yang hilang ketika saya membuat Kunci Asing menggunakan `WITH NOCHECK`?

11

Saya tahu bahwa jika saya melakukan EXISTS()panggilan pada nilai pencarian FK, maka, jika kendala FK dipercaya, hasilnya langsung.

Dan jika tidak dipercaya (seperti ketika saya membuat FK menggunakan WITH NOCHECK) maka SQL Server harus pergi dan periksa ke tabel untuk melihat apakah nilainya benar-benar ada.

Apakah ada hal lain yang saya hilangkan dengan menggunakan NOCHECK?

Gunung berapi
sumber

Jawaban:

13

Seperti yang telah Anda temukan dengan existscontoh Anda , SQL Server dapat menggunakan fakta bahwa kunci asing dipercaya ketika rencana kueri dibuat.

Apakah ada hal lain yang saya kehilangan dengan menggunakan NOCHECK?

Terlepas dari kenyataan bahwa Anda dapat menambahkan nilai ke kolom yang seharusnya tidak ada di sana seperti dijawab oleh Ste Bov Anda akan memiliki lebih banyak skenario di mana rencana kueri akan lebih baik ketika kunci asing dipercaya.

Berikut adalah satu contoh dengan tampilan yang diindeks .

Anda memiliki dua tabel dengan batasan FK tepercaya.

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

Tidak ada banyak negara tetapi trilyun kota dan beberapa dari mereka adalah kota besar.

Contoh data:

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

Kueri yang paling sering dieksekusi dalam aplikasi ini terkait dengan menemukan jumlah kota besar per negara. Untuk mempercepat dengan itu kami menambahkan tampilan yang diindeks.

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

Setelah beberapa saat muncul permintaan untuk menambah negara baru

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

Paket kueri untuk sisipan itu tidak memiliki kejutan.

masukkan deskripsi gambar di sini

Masukkan indeks berkerumun ke Countrytabel.

Sekarang, jika kunci asing Anda tidak dipercaya

alter table dbo.City nocheck constraint FK_CountryID;

dan Anda menambahkan negara baru

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

Anda akan berakhir dengan gambar yang tidak begitu cantik ini.

masukkan deskripsi gambar di sini

Cabang bawah ada untuk memperbarui tampilan yang diindeks. Itu memindai tabel penuh Cityuntuk mengetahui apakah negara dengan CountryID = 5sudah memiliki baris dalam tabel City.

Ketika kunci dipercaya, SQL Server tahu tidak ada baris Cityyang cocok dengan baris baru di Country.

Mikael Eriksson
sumber
4

Anda kehilangan optimasi kueri. Dalam praktiknya, satu-satunya optimisasi yang saya ingat adalah eliminasi redundant joins. Misalnya jika Anda memiliki pandangan:

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

Dan ketika menggunakan tampilan Anda tidak menggunakan kolom cmaka yang bergabung dapat dihapus jika ada pengaturan FK yang tepat.

EXISTSContoh Anda adalah kasus khusus untuk menghapus gabungan yang berlebihan. Saya tidak berpikir bahwa contoh khusus itu praktis relevan.

Anda juga kehilangan integritas data ketat yang disediakan oleh kendala tepercaya.

usr
sumber
3

Opsi NOCHECK melakukan apa yang tertulis di kaleng.

Ini terutama digunakan untuk menambahkan kunci asing setengah jalan melalui keberadaan tabel di mana ada hubungan baru yang mungkin tidak diperlukan (itulah pemahaman saya setidaknya).

Ini berarti bahwa kolom yang memiliki kunci asing MUNGKIN memiliki nilai di dalamnya yang tidak berkorelasi dengan nilai yang ditunjuk yang harus terkait.

Ini berarti bahwa ketika Anda memiliki opsi NOCHECK di SQL Server harus benar-benar pergi dan memeriksa apakah nilai kunci itu sebenarnya adalah kunci utama. Jika NOCHECK tidak disetel maka SQL Server menganggap bahwa apa pun yang ada di kolom itu pasti ada karena entri tidak bisa ada dalam tabel jika itu belum menjadi kunci utama, dan Anda tidak bisa menghapus kunci utama tanpa menghapus baris di pertanyaan.

Simply NOCHECK adalah kunci asing yang tidak dapat Anda percayai untuk benar-benar berhubungan dengan apa pun.

Anda sebenarnya tidak kehilangan apa pun selain kepercayaan bahwa kunci utama dijamin ada di sana.

Ste Bov
sumber
Tidak jelas dari jawaban Anda apakah ada penegakan kunci asing setelah pembuatan awal kendala. Bisakah Anda memperjelas apa yang terjadi jika Anda mencoba INSERTbaris baru yang terkait dengan baris induk yang tidak ada atau jika Anda mencoba DELETEbaris yang memiliki baris anak nanti?
jpmc26