Foreign Key เป็นคีย์ที่ไม่ใช่คีย์หลัก


143

ฉันมีตารางที่เก็บข้อมูลและหนึ่งในแถวนั้นจำเป็นต้องมีอยู่ในตารางอื่น ดังนั้นฉันต้องการคีย์ต่างประเทศเพื่อรักษาความสมบูรณ์ของการอ้างอิง

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

อย่างไรก็ตามอย่างที่คุณเห็นตารางที่ I Foreign Key ถึงคอลัมน์นั้นไม่ใช่ PK มีวิธีสร้างคีย์ต่างประเทศนี้หรืออาจเป็นวิธีที่ดีกว่าในการรักษาความสมบูรณ์ของการอ้างอิงนี้ไว้


มันไม่ค่อยสมเหตุสมผลที่จะทำเช่นนั้น ทำไมไม่อ้างถึงtable1.ID?
zerkms

เป็นที่ชัดเจนว่าหาก AnothidID ของคุณไม่ใช่คีย์หลักควรเป็น ForeignKey ดังนั้นการเป็น ForeignKey table2 ของคุณควรชี้ไปที่ตารางเดียวกัน (เป็นไปได้ table3)
Roger Barreto

คำตอบ:


193

หากคุณต้องการสร้างคีย์นอกให้กับคีย์ที่ไม่ใช่คีย์หลักจริงๆคอลัมน์นั้นต้องเป็นคอลัมน์ที่มีข้อ จำกัด เฉพาะ

จากหนังสือออนไลน์ :

ข้อ จำกัด ของคีย์ต่างประเทศไม่จำเป็นต้องเชื่อมโยงกับข้อ จำกัด ของคีย์หลักในตารางอื่นเท่านั้น นอกจากนี้ยังสามารถกำหนดให้อ้างอิงคอลัมน์ของข้อ จำกัด UNIQUE ในตารางอื่นได้

ดังนั้นในกรณีของคุณหากคุณสร้างAnotherIDเอกลักษณ์ก็จะได้รับอนุญาต หากคุณไม่สามารถใช้ข้อ จำกัด ที่เป็นเอกลักษณ์ได้แสดงว่าคุณโชคไม่ดี แต่สิ่งนี้ก็สมเหตุสมผลหากคุณคิดถึงมัน

แม้ว่าดังที่ได้กล่าวไปแล้วหากคุณมีคีย์หลักที่ดีอย่างสมบูรณ์เป็นคีย์ตัวเลือกทำไมไม่ใช้สิ่งนั้น


2
เกี่ยวข้องกับคำถามสุดท้ายของคุณ ... ฉันมีสถานการณ์ที่ฉันต้องการให้คีย์ตัวเลือกแบบผสมเป็นคีย์หลักเพียงเพราะว่ามันมีความสำคัญมากกว่าและอธิบายโมเดลของฉันได้ดีที่สุด ฉันก็ต้องการให้มีการอ้างอิงคีย์ต่างประเทศซึ่งเป็นคีย์ตัวแทนที่สร้างขึ้นใหม่เพื่อประโยชน์ในการทำงาน (ตามที่ระบุไว้ข้างต้น) มีใครคาดการณ์ปัญหาเกี่ยวกับการตั้งค่าดังกล่าวหรือไม่?
Daniel Macias

ท่านช่วยบอกได้ไหมว่าตรรกะเบื้องหลังคีย์ต่างประเทศนั้นอ้างอิงแอตทริบิวต์ด้วยข้อ จำกัด เฉพาะอย่างไร
Shivangi Gupta

วิธีทำใน asp net MVC 5
irfandar

จำนวนเต็มปกติที่ไม่ใช่คีย์หลักสามารถประกาศคีย์ต่างประเทศในตารางอื่นได้หรือไม่ ชอบอันนี้. เป็นไปได้ไหม สร้างโครงการตาราง (PSLNO Numeric (8,0) ไม่ใช่ Null, PrMan Numeric (8,0), StEng Numeric (8,0), CONSTRAINT PK_Project PRIMARY KEY (PSLNO), CONSTRAINT FK_Project1 FOREIGN KEY (PrMan) พนักงานอ้างอิง (EmpID) , CONSTRAINT FK_Project2 FOREIGN KEY (StEng) REFERENCES Employee (EmpID),)
Nabid

20

ตามที่คนอื่น ๆ ได้ชี้ให้เห็นโดยหลักการแล้ว Foreign Key จะถูกสร้างขึ้นเพื่ออ้างอิงถึงคีย์หลัก (โดยปกติคือคอลัมน์ IDENTITY) อย่างไรก็ตามเราไม่ได้อยู่ในโลกแห่งอุดมคติและบางครั้งแม้แต่การเปลี่ยนแปลงสคีมาเพียงเล็กน้อยก็อาจส่งผลกระทบอย่างมีนัยสำคัญต่อตรรกะของแอปพลิเคชันได้

พิจารณากรณีของตารางลูกค้าที่มีคอลัมน์ SSN (และคีย์หลักที่เป็นใบ้) และตารางการอ้างสิทธิ์ที่มีคอลัมน์ SSN ด้วย (สร้างโดยตรรกะทางธุรกิจจากข้อมูลลูกค้า แต่ไม่มี FK อยู่) การออกแบบมีข้อบกพร่อง แต่ใช้งานมาหลายปีแล้วและมีการสร้างแอปพลิเคชั่นที่แตกต่างกันสามแบบบนสคีมา ควรจะเห็นได้ชัดว่าการริบ Claim.SSN และใส่ความสัมพันธ์ PK-FK ที่แท้จริงจะเป็นสิ่งที่ดี แต่ก็จะเป็นการยกเครื่องครั้งสำคัญเช่นกัน ในทางกลับกันการวางข้อ จำกัด ที่ไม่ซ้ำใครกับ Customer.SSN และการเพิ่ม FK ในการอ้างสิทธิ์ SSN สามารถให้ความสมบูรณ์ของการอ้างอิงโดยมีผลกระทบเพียงเล็กน้อยหรือไม่มีเลยกับแอปพลิเคชัน

อย่าเข้าใจว่าฉันผิดฉันทุกคนต้องการการทำให้เป็นมาตรฐาน แต่บางครั้งลัทธิปฏิบัตินิยมก็ชนะความเพ้อฝัน หากการออกแบบที่เรียบง่ายสามารถใช้เครื่องช่วยรัดได้อาจหลีกเลี่ยงการผ่าตัด


18

เนโครแมนซิ่ง.
ฉันคิดว่าเมื่อมีคนมาถึงที่นี่เขาต้องการคีย์ต่างประเทศในคอลัมน์ในตารางที่มีคีย์ที่ไม่ซ้ำกัน

ปัญหาคือถ้าคุณมีปัญหานั้นฐานข้อมูลสคีมาจะถูกทำให้เป็นปกติ

ตัวอย่างเช่นคุณเก็บห้องไว้ในตารางโดยมีคีย์หลักของ room-uid, ฟิลด์ DateFrom และ DateTo และ uid อื่นที่นี่ RM_ApertureID เพื่อติดตามห้องเดียวกันและช่อง soft-delete เช่น RM_Status โดยที่ 99 หมายถึง 'ลบ' และ <> 99 หมายถึง 'ใช้งานอยู่'

ดังนั้นเมื่อคุณสร้างห้องแรกให้ใส่ RM_UID และ RM_ApertureID เป็นค่าเดียวกับ RM_UID จากนั้นเมื่อคุณยุติห้องเป็นวันที่และสร้างใหม่ด้วยช่วงวันที่ใหม่ RM_UID คือ newid () และ RM_ApertureID จากรายการก่อนหน้าจะกลายเป็น RM_ApertureID ใหม่

ดังนั้นหากเป็นเช่นนั้น RM_ApertureID เป็นฟิลด์ที่ไม่ซ้ำกันดังนั้นคุณจึงไม่สามารถตั้งค่าคีย์นอกในตารางอื่น

และไม่มีวิธีใดในการตั้งค่า Foreign Key ให้กับคอลัมน์ / ดัชนีที่ไม่ซ้ำกันเช่นใน T_ZO_REM_AP_Raum_Reinigung (WHERE 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

3

คีย์หลักต้องไม่ซ้ำกันเสมอคีย์ต่างประเทศต้องอนุญาตค่าที่ไม่ซ้ำกันหากตารางเป็นความสัมพันธ์แบบหนึ่งต่อกลุ่ม เป็นการดีอย่างยิ่งที่จะใช้ Foreign Key เป็นคีย์หลักหากตารางเชื่อมต่อด้วยความสัมพันธ์แบบหนึ่งต่อกลุ่มไม่ใช่ความสัมพันธ์แบบหนึ่งต่อกลุ่ม

ข้อ จำกัด ของคีย์ต่างประเทศไม่จำเป็นต้องเชื่อมโยงกับข้อ จำกัด ของคีย์หลักในตารางอื่นเท่านั้น นอกจากนี้ยังสามารถกำหนดให้อ้างอิงคอลัมน์ของข้อ จำกัด UNIQUE ในตารางอื่นได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.