गैर-प्राथमिक कुंजी के लिए विदेशी कुंजी


136

मेरे पास एक तालिका है जो डेटा रखती है, और उन पंक्तियों में से एक को दूसरी तालिका में मौजूद होना चाहिए। इसलिए, मुझे संदर्भात्मक अखंडता बनाए रखने के लिए एक विदेशी कुंजी चाहिए।

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

हालाँकि, जैसा कि आप देख सकते हैं, मैं जिस तालिका की कुंजी है, वह कॉलम PK नहीं है। क्या इस विदेशी कुंजी को बनाने का एक तरीका है, या शायद इस संदर्भ अखंडता को बनाए रखने का एक बेहतर तरीका है?


यह करने के लिए बहुत समझ में नहीं आता है। रेफर क्यों नहीं किया table1.ID?
झटके

यह निश्चित है कि यदि आपका एनॉथिड एक प्राथमिक कुंजी नहीं है, तो यह एक फॉरेनकेई होना चाहिए, इसलिए एक फॉरेनकेई होने के नाते, आपका टेबल 2 एक ही टेबल (संभावित टेबल 3) की ओर इशारा करना चाहिए
रोजर बैरेटो

जवाबों:


182

यदि आप वास्तव में एक गैर-प्राथमिक कुंजी के लिए एक विदेशी कुंजी बनाना चाहते हैं, तो यह एक स्तंभ होना चाहिए, जिस पर एक अद्वितीय बाधा है।

ऑनलाइन पुस्तकों से :

एक पूर्व प्रमुख बाधा को केवल एक अन्य तालिका में एक प्राथमिक कुंजी बाधा से जोड़ा जाना नहीं है; इसे एक अन्य तालिका में एक UNIQUE बाधा के स्तंभों को संदर्भित करने के लिए भी परिभाषित किया जा सकता है।

तो आपके मामले में यदि आप AnotherIDअद्वितीय बनाते हैं, तो इसकी अनुमति होगी। यदि आप एक अद्वितीय बाधा लागू नहीं कर सकते हैं तो आप भाग्य से बाहर हैं, लेकिन अगर आप इसके बारे में सोचते हैं तो यह वास्तव में समझ में नहीं आता है।

यद्यपि, जैसा कि उल्लेख किया गया है, यदि आपके पास उम्मीदवार कुंजी के रूप में पूरी तरह से अच्छी प्राथमिक कुंजी है, तो इसका उपयोग क्यों नहीं करें?


1
अपने पिछले प्रश्न से संबंधित ... मैं एक स्थिति जहां मैं चाहूँगा समग्र उम्मीदवार कुंजी प्राथमिक कुंजी होना है बस , क्योंकि यह अर्थ की दृष्टि से अधिक महत्व है और सबसे अच्छा अपने मॉडल का वर्णन है। मैं भी प्रदर्शन के लिए एक विदेशी कुंजी संदर्भ एक नई बनाई गई सरोगेट कुंजी रखना चाहूंगा (जैसा कि ऊपर उल्लेख किया गया है)। किसी को भी इस तरह के एक सेटअप के साथ कोई समस्या नहीं है?
डैनियल मैकियास

सर क्या आप कृपया बता सकते हैं कि उस विदेशी कुंजी के पीछे क्या तर्क हमेशा अद्वितीय बाधा के साथ विशेषता का संदर्भ देता है?
शिवांगी गुप्ता

एस्प नेट MVC 5 में यह कैसे करें
irfandar

क्या सामान्य गैर प्राथमिक कुंजी पूर्णांक को अन्य तालिका में विदेशी कुंजी घोषित किया जा सकता है? इस तरह। क्या यह संभव है? रचनात्मक तालिका परियोजना (PSLNO न्यूमेरिक (8,0) नहीं अशक्त, प्रमान न्यूमेरिक (8,0), स्टैंग न्यूमेरिक (8,0), CONSTRAINT PK_Project PRIMARY KEY (PSLNO), CONFRAINT FK_Project1 FOREIGN KEY (PrMan) REFERENCES Employee (EmpID) बाधा FK_Project2 विदेशी कुँजी (StEng) संदर्भ कर्मचारी (EmpID),)
Nabid

19

जैसा कि दूसरों ने बताया है, आदर्श रूप से, विदेशी कुंजी एक प्राथमिक कुंजी (आमतौर पर एक पहचान कॉलम) के संदर्भ के रूप में बनाई जाएगी। हालाँकि, हम एक आदर्श दुनिया में नहीं रहते हैं, और कभी-कभी स्कीमा में "छोटे" परिवर्तन से एप्लिकेशन लॉजिक के लिए महत्वपूर्ण तरंग प्रभाव हो सकते हैं।

SSN कॉलम (और डंब प्राथमिक कुंजी) के साथ ग्राहक तालिका के मामले पर विचार करें, और एक दावा तालिका जिसमें SSN कॉलम भी शामिल है (ग्राहक डेटा से व्यावसायिक तर्क द्वारा पॉप्युलेट किया गया, लेकिन कोई FK मौजूद नहीं है)। डिज़ाइन त्रुटिपूर्ण है, लेकिन कई वर्षों से उपयोग में है, और स्कीमा पर तीन अलग-अलग एप्लिकेशन बनाए गए हैं। यह स्पष्ट होना चाहिए कि क्लेम.एसएसएन को खत्म करना और एक वास्तविक पीके-एफके रिश्ते में डालना आदर्श होगा, लेकिन एक महत्वपूर्ण ओवरहाल भी होगा । दूसरी ओर, Customer.SSN पर UNIQUE बाधा डालते हुए, और Claim.SSN पर एक FK जोड़कर, अनुप्रयोगों पर बहुत कम या कोई प्रभाव न होने के साथ, संदर्भात्मक अखंडता प्रदान कर सकता है।

मुझे गलत मत समझो, मैं सामान्यीकरण के लिए हूं, लेकिन कभी-कभी व्यावहारिकता आदर्शवाद पर जीत हासिल करती है। यदि बैंड-सहायता से एक औसत दर्जे की डिज़ाइन की मदद की जा सकती है, तो सर्जरी से बचा जा सकता है।


18

Necromancing।
मुझे लगता है कि जब कोई व्यक्ति यहां उतरता है, तो उसे एक गैर-विशिष्ट कुंजी वाली तालिका में कॉलम के लिए एक विदेशी कुंजी की आवश्यकता होती है।

समस्या यह है, कि यदि आपके पास वह समस्या है, तो डेटाबेस-स्कीमा को अपभ्रंश किया जाता है।

आप उदाहरण के लिए एक टेबल में कमरे रखने के लिए हैं, एक रूम-यूआईडी प्राइमरी की, एक DateFrom और एक DateTo फ़ील्ड, और दूसरा यूआईडी, यहाँ RM_ApertureID एक ही कमरे का ट्रैक रखने के लिए, और एक सॉफ्ट-डिलीट फ़ील्ड, जैसे RM_Status, जहाँ 99 का अर्थ है 'हटा दिया गया', और <> 99 का अर्थ है 'सक्रिय'।

इसलिए जब आप पहला कमरा बनाते हैं, तो आप RM_UID और RM_UpertureID को RM_UID के समान मान के साथ सम्मिलित करते हैं। फिर, जब आप कमरे को एक तारीख को समाप्त करते हैं, और इसे एक नई तिथि सीमा के साथ फिर से स्थापित करते हैं, तो RM_UID नया है (), और पिछली प्रविष्टि से RM_ApertureID नया RM_ApertureID बन जाता है।

इसलिए, यदि ऐसा है, तो RM_ApertureID एक गैर-विशिष्ट फ़ील्ड है, और इसलिए आप किसी अन्य तालिका में एक विदेशी-कुंजी सेट नहीं कर सकते।

और एक गैर-अद्वितीय कॉलम / इंडेक्स के लिए एक विदेशी कुंजी सेट करने का कोई तरीका नहीं है, उदाहरण के लिए T_ZO_REM_AP_Raum_Reinigung (जहां RM_UID वास्तव में RM_ApertureID है)।
लेकिन अमान्य मूल्यों पर रोक लगाने के लिए, आपको एक विदेशी कुंजी सेट करने की आवश्यकता है, अन्यथा, डेटा-कचरा बाद में के बजाय जल्द ही परिणाम है ...

अब आप इस मामले में क्या कर सकते हैं (पूरे आवेदन को फिर से लिखने में कमी) एक CHECK- बाधा डाल रहा है, एक स्केलर फ़ंक्शन के साथ कुंजी की उपस्थिति की जांच कर रहा है:

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

हमेशा पार्टी के लिए देर से ... लेकिन इस वास्तविक दुनिया की सलाह के लिए धन्यवाद - मेरे पास वास्तव में है - माध्यमिक तालिका में डेटा संस्करणित है (एक कुंजी के अलावा एक तिथि सीमा है), और मैं केवल नवीनतम संस्करण लिंक करना चाहता हूं मेरी प्राथमिक तालिका से ...
इयान

1
अच्छी वास्तविक दुनिया की सलाह! मैं विरासत अनुप्रयोगों के साथ बहुत सारे परिदृश्यों की कल्पना कर सकता हूं जहां "सर्वश्रेष्ठ अभ्यास" एक कारण या किसी अन्य के लिए संभव नहीं है, और चेक बाधा अच्छी तरह से काम करेगी।
ryanwc

यह समाधान अविश्वसनीय है। देखें: dba.stackexchange.com/…/how-are-my-sql-server-constraints-being-bypassed
stomy

2

प्राथमिक कुंजियों को हमेशा अद्वितीय होने की आवश्यकता होती है, विदेशी कुंजियों को गैर-अद्वितीय मानों की अनुमति देने की आवश्यकता होती है यदि तालिका एक-से-कई संबंध है। यह एक प्राथमिक कुंजी के रूप में एक विदेशी कुंजी का उपयोग करने के लिए पूरी तरह से ठीक है यदि तालिका एक-से-एक संबंध से जुड़ी है, एक से कई संबंधों से नहीं।

एक पूर्व प्रमुख बाधा को केवल एक अन्य तालिका में एक प्राथमिक कुंजी बाधा से जोड़ा जाना नहीं है; इसे एक अन्य तालिका में एक UNIQUE बाधा के स्तंभों को संदर्भित करने के लिए भी परिभाषित किया जा सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.