Birincil olmayan anahtara yabancı anahtar


136

Veri tutan bir tablo var ve bu satırlardan biri başka bir tabloda olması gerekir. Bu yüzden, referans bütünlüğünü korumak için yabancı bir anahtar istiyorum.

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

Ancak, gördüğünüz gibi, benim yabancı anahtar tablo, sütun PK değil. Bu yabancı anahtarı oluşturmanın bir yolu var mı, ya da bu referans bütünlüğünü korumanın daha iyi bir yolu var mı?


Bunu yapmak pek mantıklı değil. Neden başvurmuyorsunuz table1.ID?
zerkms

AnothidID'niz birincil anahtar değilse, bu bir ForeignKey olmalıdır, bu yüzden bir ForeignKey olarak, table2'niz aynı tabloyu göstermelidir (olası tablo3)
Roger Barreto

Yanıtlar:


182

Birincil olmayan bir anahtar için gerçekten bir yabancı anahtar oluşturmak istiyorsanız, üzerinde benzersiz bir kısıtlaması olan bir sütun OLMALIDIR.

Gönderen Books Online :

Bir FOREIGN KEY kısıtlamasının yalnızca başka bir tablodaki PRIMARY KEY kısıtlamasına bağlanması gerekmez; başka bir tabloda UNIQUE sınırlamasının sütunlarına başvuruda bulunmak için de tanımlanabilir.

Yani sizin durumunuzda AnotherIDbenzersiz yaparsanız , buna izin verilecektir. Benzersiz bir kısıtlama uygulayamıyorsanız, şansınız kalmaz, ancak bunu düşünürseniz gerçekten mantıklıdır.

Belirtildiği gibi, aday anahtar olarak mükemmel derecede iyi bir birincil anahtarınız varsa, neden bunu kullanmıyorsunuz?


1
Son soruya İlgili ... Ben kompozit aday tuşları birincil anahtar olmak istiyorum bir durum var sadece o anlam daha öneme sahiptir ve en iyi benim modeli açıklar çünkü. Ben de performans için yeni oluşturulmuş bir vekil anahtar yabancı bir referans istiyorum (yukarıda belirtildiği gibi). Herkes böyle bir kurulum ile herhangi bir sorun öngörüyor mu?
Daniel Macias

Efendim, yabancı anahtarın ardındaki mantığın her zaman özel kısıtlama ile özniteliğe atıfta bulunduğunu söyleyebilir misiniz?
Shivangi Gupta

Nasıl asp net MVC 5
irfandar

Normal olmayan birincil anahtar tamsayısı diğer tabloda yabancı anahtar olarak bildirilebilir mi? Bunun gibi. Mümkün mü? TABLO OLUŞTURMA Projesi (PSLNO Sayısal (8,0) Boş Değil, PrMan Sayısal (8,0), StEng Sayısal (8,0), CONSTRAINT PK_Project PRIMARY KEY (PSLNO), CONSTRAINT FK_Project1 FOREIGN KEY (PrMan) REFERANSLAR Çalışan (EmpID) , CONSTRAINT FK_Project2 YABANCI ANAHTAR (StEng) REFERANSLAR Çalışan (EmpID),)
Nabid

19

Diğerlerinin işaret ettiği gibi, ideal olarak, yabancı anahtar birincil anahtara (genellikle bir KİMLİK sütunu) referans olarak yaratılır. Bununla birlikte, ideal bir dünyada yaşamıyoruz ve bazen bir şemadaki "küçük" bir değişiklik bile uygulama mantığında önemli dalgalanma etkilerine sahip olabilir.

SSN sütunu (ve dilsiz birincil anahtar) olan bir Müşteri tablosunu ve ayrıca bir SSN sütunu içeren bir Talep tablosunu (Müşteri verilerinden iş mantığı tarafından doldurulur, ancak FK yok) düşünün. Tasarım kusurludur, ancak birkaç yıldır kullanılmaktadır ve şema üzerine üç farklı uygulama inşa edilmiştir. Claim.SSN'nin çıkarılması ve gerçek bir PK-FK ilişkisi kurmanın ideal olacağı açıktır, ancak aynı zamanda önemli bir revizyon olacaktır. Öte yandan, Customer.SSN üzerinde EŞSİZ bir kısıtlama koymak ve Claim.SSN'ye bir FK eklemek, uygulamalar üzerinde çok az etkisi olan veya hiç etkisi olmayan referans bütünlüğü sağlayabilir.

Beni yanlış anlamayın, ben normalleşme içindeyim, ama bazen pragmatizm idealizmi kazanır. Yara bandı tasarımı vasat bir tasarıma yardımcı olabilirse, ameliyattan kaçınılabilir.


18

Necromancing.
Birisi buraya geldiğinde, benzersiz olmayan anahtarlar içeren bir tabloda sütun için yabancı bir anahtara ihtiyacı olduğunu varsayıyorum.

Sorun, bu sorun varsa, veritabanı şemasının denormalized olmasıdır.

Örneğin, odaları aynı odada izlemek için bir oda-uid birincil anahtarı, bir DateFrom ve bir DateTo alanı ve başka bir uid, RM_ApertureID ve RM_Status gibi bir yumuşak silme alanı, burada 99 'silinmiş' anlamına gelir ve <> 99 'aktif' anlamına gelir.

İlk odayı oluşturduğunuzda, RM_UID ve RM_ApertureID değerlerini RM_UID ile aynı değer olarak eklersiniz. Ardından, odayı bir tarihe sonlandırıp yeni bir tarih aralığıyla yeniden oluşturduğunuzda, RM_UID newid () olur ve önceki girişten RM_ApertureID değeri yeni RM_ApertureID olur.

Bu durumda, RM_ApertureID benzersiz olmayan bir alandır ve başka bir tabloda yabancı anahtar ayarlayamazsınız.

Benzersiz olmayan bir sütuna / dizine yabancı anahtar ayarlamanın bir yolu yoktur; örneğin, T_ZO_REM_AP_Raum_Reinigung'da (WHERE RM_UID aslında RM_ApertureID'dir).
Ancak geçersiz değerleri yasaklamak için yabancı bir anahtar ayarlamanız gerekir, aksi takdirde veri çöpü daha sonra değil, daha erken sonuçtur ...

Şimdi bu durumda yapabilecekleriniz (tüm uygulamayı yeniden yazmanın kısa bir yolu), anahtarın varlığını kontrol eden bir skaler fonksiyon ile bir CHECK kısıtlaması ekliyor:

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

Partiye her zaman geç ... Ama bu gerçek dünya tavsiyesi için teşekkürler - tam olarak bu var - ikincil tablodaki veriler sürümlendirilmiş (bir anahtara ek olarak bir tarih aralığı var) ve sadece en son sürümü bağlamak istiyorum birincil
Ian

1
Güzel gerçek dünya tavsiyesi! "En iyi uygulamanın" bir nedenden ötürü mümkün olmadığı ve kontrol kısıtlamasının iyi çalışacağı eski uygulamalarla ilgili çok sayıda senaryo hayal edebiliyorum.
ryanwc


2

Birincil anahtarların her zaman benzersiz olması gerekir; yabancı anahtarlar, tablo bire çok ilişkisiyse benzersiz olmayan değerlere izin vermelidir. Tablonun bire çok ilişkisi değil, bire bir ilişki ile bağlanması durumunda, yabancı anahtarı birincil anahtar olarak kullanmak son derece iyidir.

Bir FOREIGN KEY kısıtlamasının yalnızca başka bir tablodaki PRIMARY KEY kısıtlamasına bağlanması gerekmez; başka bir tabloda UNIQUE sınırlamasının sütunlarına başvuruda bulunmak için de tanımlanabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.