Saya memiliki tabel yang menyimpan data, dan salah satu baris tersebut harus ada di tabel lain. Jadi, saya ingin kunci asing untuk menjaga integritas referensial.
CREATE TABLE table1
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AnotherID INT NOT NULL,
SomeData VARCHAR(100) NOT NULL
)
CREATE TABLE table2
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AnotherID INT NOT NULL,
MoreData VARCHAR(30) NOT NULL,
CONSTRAINT fk_table2_table1 FOREIGN KEY (AnotherID) REFERENCES table1 (AnotherID)
)
Namun, seperti yang Anda lihat, tabel I foreign key, kolomnya bukan PK. Apakah ada cara untuk membuat kunci asing ini, atau mungkin cara yang lebih baik untuk menjaga integritas referensial ini?
sql
sql-server
Craig
sumber
sumber
table1.ID
?Jawaban:
Jika Anda benar-benar ingin membuat kunci asing ke kunci non-primer, itu HARUS berupa kolom yang memiliki batasan unik.
Dari Buku Online :
Jadi dalam kasus Anda jika Anda membuatnya
AnotherID
unik, itu akan diizinkan. Jika Anda tidak dapat menerapkan batasan unik, Anda kurang beruntung, tetapi ini benar-benar masuk akal jika Anda memikirkannya.Meskipun, seperti yang telah disebutkan, jika Anda memiliki kunci utama yang sangat bagus sebagai kunci kandidat, mengapa tidak menggunakannya?
sumber
Seperti yang ditunjukkan orang lain, idealnya, kunci asing akan dibuat sebagai referensi ke kunci utama (biasanya kolom IDENTITAS). Namun, kita tidak hidup di dunia yang ideal, dan terkadang bahkan perubahan "kecil" pada skema dapat memiliki efek riak yang signifikan pada logika aplikasi.
Pertimbangkan kasus tabel Pelanggan dengan kolom SSN (dan kunci utama bodoh), dan tabel Klaim yang juga berisi kolom SSN (diisi oleh logika bisnis dari data Pelanggan, tetapi tidak ada FK). Desainnya cacat, tetapi telah digunakan selama beberapa tahun, dan tiga aplikasi berbeda telah dibangun di atas skema tersebut. Jelas bahwa merobek Claim.SSN dan membuat hubungan PK-FK yang sebenarnya akan ideal, tetapi juga akan menjadi perbaikan yang signifikan . Di sisi lain, menempatkan batasan UNIK pada Customer.SSN, dan menambahkan FK pada Claim.SSN, dapat memberikan integritas referensial, dengan sedikit atau tidak ada dampak pada aplikasi.
Jangan salah paham, saya mendukung normalisasi, tetapi terkadang pragmatisme menang atas idealisme. Jika desain yang biasa-biasa saja dapat dibantu dengan perban, pembedahan dapat dihindari.
sumber
Necromancing.
Saya berasumsi ketika seseorang mendarat di sini, dia membutuhkan kunci asing ke kolom dalam tabel yang berisi kunci non-unik.
Masalahnya adalah, jika Anda memiliki masalah itu, skema database didenormalisasi.
Anda misalnya menyimpan ruangan dalam sebuah meja, dengan kunci utama kamar-uid, bidang DateFrom dan DateTo, dan uid lain, di sini RM_ApertureID untuk melacak ruangan yang sama, dan bidang hapus lembut, seperti RM_Status, dimana 99 berarti 'dihapus', dan <> 99 berarti 'aktif'.
Jadi saat Anda membuat ruang pertama, Anda memasukkan RM_UID dan RM_ApertureID sebagai nilai yang sama dengan RM_UID. Kemudian, saat Anda menghentikan ruangan ke tanggal, dan membuatnya kembali dengan rentang tanggal baru, RM_UID adalah newid (), dan RM_ApertureID dari entri sebelumnya menjadi RM_ApertureID baru.
Jadi, jika demikian, RM_ApertureID adalah bidang non-unik, sehingga Anda tidak dapat menyetel kunci asing di tabel lain.
Dan tidak ada cara untuk menyetel kunci asing ke kolom / indeks non-unik, misalnya di T_ZO_REM_AP_Raum_Reinigung (DI MANA RM_UID sebenarnya adalah RM_ApertureID).
Tetapi untuk melarang nilai yang tidak valid, Anda perlu menyetel kunci asing, jika tidak, data-sampah adalah hasilnya lebih cepat daripada nanti ...
Sekarang yang dapat Anda lakukan dalam kasus ini (singkatnya menulis ulang seluruh aplikasi) adalah memasukkan batasan PERIKSA, dengan fungsi skalar memeriksa keberadaan kunci:
IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]')) ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung DROP CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fu_Constaint_ValidRmApertureId]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION [dbo].[fu_Constaint_ValidRmApertureId] GO CREATE FUNCTION [dbo].[fu_Constaint_ValidRmApertureId]( @in_RM_ApertureID uniqueidentifier ,@in_DatumVon AS datetime ,@in_DatumBis AS datetime ,@in_Status AS integer ) RETURNS bit AS BEGIN DECLARE @bNoCheckForThisCustomer AS bit DECLARE @bIsInvalidValue AS bit SET @bNoCheckForThisCustomer = 'false' SET @bIsInvalidValue = 'false' IF @in_Status = 99 RETURN 'false' IF @in_DatumVon > @in_DatumBis BEGIN RETURN 'true' END IF @bNoCheckForThisCustomer = 'true' RETURN @bIsInvalidValue IF NOT EXISTS ( SELECT T_Raum.RM_UID ,T_Raum.RM_Status ,T_Raum.RM_DatumVon ,T_Raum.RM_DatumBis ,T_Raum.RM_ApertureID FROM T_Raum WHERE (1=1) AND T_Raum.RM_ApertureID = @in_RM_ApertureID AND @in_DatumVon >= T_Raum.RM_DatumVon AND @in_DatumBis <= T_Raum.RM_DatumBis AND T_Raum.RM_Status <> 99 ) SET @bIsInvalidValue = 'true' -- IF ! RETURN @bIsInvalidValue END GO IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]')) ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung DROP CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] GO -- ALTER TABLE dbo.T_AP_Kontakte WITH CHECK ADD CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung WITH NOCHECK ADD CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] CHECK ( NOT ( dbo.fu_Constaint_ValidRmApertureId(ZO_RMREM_RM_UID, ZO_RMREM_GueltigVon, ZO_RMREM_GueltigBis, ZO_RMREM_Status) = 1 ) ) GO IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]')) ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung CHECK CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung] GO
sumber
Kunci utama selalu harus unik, kunci asing harus mengizinkan nilai non-unik jika tabel adalah hubungan satu ke banyak. Tidak masalah menggunakan kunci asing sebagai kunci utama jika tabel dihubungkan dengan hubungan satu-ke-satu, bukan hubungan satu-ke-banyak.
Batasan FOREIGN KEY tidak harus ditautkan hanya ke batasan PRIMARY KEY di tabel lain; itu juga dapat didefinisikan untuk mereferensikan kolom kendala UNIK di tabel lain.
sumber