Foreign Key หมายถึงคีย์หลักในหลายตาราง?


92

ฉันต้องมีสองตารางคือพนักงาน _ce และพนักงาน _ ภายใต้ฐานข้อมูลพนักงาน

ทั้งสองมีคอลัมน์คีย์หลักที่ไม่ซ้ำกันตามลำดับ

ฉันมีตารางอื่นที่เรียกว่าการหักเงินซึ่งมีคอลัมน์คีย์ต่างประเทศที่ฉันต้องการอ้างอิงถึงคีย์หลักของพนักงาน _ce และพนักงาน _sn เป็นไปได้หรือไม่

ตัวอย่างเช่น

employees_ce
--------------
empid   name
khce1   prince

employees_sn
----------------
empid   name
khsn1   princess

เป็นไปได้ไหม

deductions
--------------
id      name
khce1   gold
khsn1   silver

คำตอบ:


99

สมมติว่าฉันเข้าใจสถานการณ์ของคุณอย่างถูกต้องนี่คือสิ่งที่ฉันเรียกว่าวิธีที่ถูกต้องในการทำสิ่งนี้:

เริ่มจากคำอธิบายระดับที่สูงขึ้นของฐานข้อมูลของคุณ! คุณมีพนักงานและพนักงานสามารถเป็นพนักงาน "ce" และ "sn" ได้ (ไม่ว่าจะเป็นอะไรก็ตาม) ในแง่เชิงวัตถุมีคลาส "พนักงาน" ซึ่งมีคลาสย่อยสองคลาสเรียกว่า "พนักงาน ce" และ "พนักงาน sn"

จากนั้นคุณแปลคำอธิบายในระดับที่สูงขึ้นนี้เพื่อสามตาราง: employees, employees_ceและemployees_sn:

  • employees(id, name)
  • employees_ce(id, ce-specific stuff)
  • employees_sn(id, sn-specific stuff)

เนื่องจากพนักงานทุกคนเป็นพนักงาน (duh!) พนักงานทุกคนจะมีแถวในemployeesโต๊ะ พนักงาน "ce" ก็มีแถวในemployees_ceโต๊ะเช่นกันและพนักงาน "sn" ก็มีแถวในemployees_snตารางเช่นกัน employees_ce.idเป็นคีย์ต่างประเทศemployees.idเช่นเดียวกับที่เป็นemployees_sn.idอยู่

หากต้องการอ้างถึงพนักงานทุกประเภท (ce หรือ sn) โปรดดูที่employeesตาราง นั่นคือคีย์ต่างประเทศที่คุณมีปัญหาควรอ้างถึงตารางนั้น!


17
คุณทำ ce และ sn แบบพิเศษร่วมกันได้อย่างไร? เนื่องจากพนักงานไม่สามารถเป็น ce และ sn ได้ในเวลาเดียวกันจึงเป็นแนวทางปฏิบัติที่ดีที่จะสะท้อนสิ่งนั้นในฐานข้อมูล ฉันกำลังมีปัญหานี้
Rolf

ฉันคิดว่าปุ่มหลายคอลัมน์สามารถช่วยแก้ปัญหาในความคิดเห็นก่อนหน้าของฉันได้ ... ค้นหาในตอนนี้
Rolf

12
คุณสามารถบังคับให้พนักงานอยู่ในตารางเดียว (และตารางที่ถูกต้อง) โดยการจัดเก็บประเภทไว้ในตารางฐานและตารางที่ได้รับ ทำให้รหัสคีย์หลักเป็นคีย์เฉพาะบน (id, type) คีย์นอกของตารางย่อยเป็น on (id, type) และใส่ข้อ จำกัด CHECK ในตารางย่อยแต่ละตารางเพื่อให้มีประเภทที่ถูกต้องเท่านั้น หรือหากฐานข้อมูลของคุณมีข้อ จำกัด ในการตรวจสอบทั่วโลก (และไม่มีการลงโทษความเร็วสูง) คุณสามารถทำการตรวจสอบ NOT EXISTS ได้แน่นอน
derobert

ตรวจสอบคำตอบนี้สำหรับคำอธิบายทั้งหมดและรายละเอียดการใช้งาน
PerformanceDBA

จะรู้ได้อย่างไรว่าพนักงานที่มี id เฉพาะคือ 'se' หรือ 'sn'?
mhrsalehi

22

คุณอาจเพิ่มข้อ จำกัด คีย์ต่างประเทศได้สองข้อ (โดยสุจริต: ฉันไม่เคยลอง) แต่จากนั้นยืนยันว่ามีแถวหลักในทั้งสองตาราง

แต่คุณอาจต้องการสร้าง supertype สำหรับประเภทย่อยของพนักงานสองคนของคุณจากนั้นชี้คีย์ต่างประเทศที่นั่นแทน (สมมติว่าคุณมีเหตุผลที่ดีที่จะแบ่งพนักงานออกเป็นสองประเภท)

                 employee       
employees_ce     ————————       employees_sn
————————————     type           ————————————
empid —————————> empid <——————— empid
name               /|\          name
                    |  
                    |  
      deductions    |  
      ——————————    |  
      empid ————————+  
      name

typeในตารางพนักงานจะเป็นหรือcesn


ฉันพยายามเพิ่มคีย์ต่างประเทศหลายคีย์พวกเขาใช้งานได้ แต่ในขณะที่เพิ่มบันทึก java derby บอกฉันว่ามีการละเมิดข้อ จำกัด ของคีย์ต่างประเทศทั้งสอง!

ฉันเพิ่งลองใช้ PostgreSQL และใช้งานได้ที่นั่น คุณมีระเบียนหลักในทั้งสองตารางหรือไม่
derobert

บันทึกผู้ปกครองที่คุณหมายถึงว่า Empid?

แน่นอนว่าปัญหาได้ถูกเปลี่ยนจากตาราง "การหักเงิน" ไปเป็นตาราง "พนักงาน" คุณจะอ้างอิงเอนทิตีที่อาจแตกต่างกันตามประเภทได้อย่างไร
gawpertron

1
@gawpertron: Empid นั้นไม่เหมือนใครในทุกประเภท คุณสามารถใช้ฟิลด์ 'type' เพื่อดูตารางย่อยที่คุณต้องการอ้างอิง หรือLEFT JOINทั้งหมดถ้ามีน้อยพอ เมื่อไม่ใช้ตารางฐาน 'พนักงาน' จะไม่สามารถประกาศคีย์หลักได้ (เพราะอ้างอิงถึง tableA หรือ tableB หรือ ... ); ตอนนี้มันสามารถ ภูมิปัญญาของการแยกemployees_ceและemployees_snถูกสันนิษฐานและข้อสันนิษฐานนั้นถูกบันทึกไว้
derobert

20

ที่จริงฉันทำเอง ฉันมีตารางที่เรียกว่า 'ความคิดเห็น' ซึ่งมีความคิดเห็นสำหรับบันทึกในอีก 3 ตาราง ทั้งสองวิธีไม่สามารถจัดการทุกสิ่งที่คุณอาจต้องการได้ ในกรณีของคุณคุณจะทำสิ่งนี้:

แนวทางที่ 1:

  1. เพิ่มเขตข้อมูลขนาดเล็กให้กับพนักงาน _ce และพนักงาน _ ที่มีค่าเริ่มต้นซึ่งแตกต่างกันในแต่ละตาราง (ฟิลด์นี้แสดงถึง 'ตัวระบุตาราง' ดังนั้นเราจะเรียกว่า tid_ce & tid_sn)

  2. สร้างดัชนีเฉพาะในแต่ละตารางโดยใช้ PK ของตารางและฟิลด์รหัสตาราง

  3. เพิ่มช่องขนาดเล็กลงในตาราง 'การหักเงิน' เพื่อจัดเก็บครึ่งหลังของคีย์ต่างประเทศ (รหัสตาราง)

  4. สร้างคีย์ต่างประเทศ 2 รายการในตาราง 'การหักเงิน' ของคุณ (คุณไม่สามารถบังคับใช้ความสมบูรณ์ของการอ้างอิงได้เนื่องจากคีย์หนึ่งจะถูกต้องหรืออีกอัน ...

    ALTER TABLE [dbo].[Deductions]  WITH NOCHECK ADD  CONSTRAINT [FK_Deductions_employees_ce] FOREIGN KEY([id], [fk_tid])
    REFERENCES [dbo].[employees_ce] ([empid], [tid])
    NOT FOR REPLICATION 
    GO
    ALTER TABLE [dbo].[Deductions] NOCHECK CONSTRAINT [FK_600_WorkComments_employees_ce]
    GO
    ALTER TABLE [dbo].[Deductions]  WITH NOCHECK ADD  CONSTRAINT [FK_Deductions_employees_sn] FOREIGN KEY([id], [fk_tid])
    REFERENCES [dbo].[employees_sn] ([empid], [tid])
    NOT FOR REPLICATION 
    GO
    ALTER TABLE [dbo].[Deductions] NOCHECK CONSTRAINT [FK_600_WorkComments_employees_sn]
    GO
    
    employees_ce
    --------------
    empid    name     tid
    khce1   prince    1
    
    employees_sn
    ----------------
    empid    name     tid 
    khsn1   princess  2
    
    deductions
    ----------------------
    id      tid       name  
    khce1   1         gold
    khsn1   2         silver         
    ** id + tid creates a unique index **
    

โซลูชันที่ 2: โซลูชันนี้ช่วยให้สามารถรักษาความสมบูรณ์ของการอ้างอิงได้: 1. สร้างฟิลด์คีย์นอกที่สองในตาราง 'การหักลบ' อนุญาตค่า Null ในคีย์ต่างประเทศทั้งสองและสร้างคีย์ต่างประเทศตามปกติ:

    employees_ce
    --------------
    empid   name
    khce1   prince 

    employees_sn
    ----------------
    empid   name     
    khsn1   princess 

    deductions
    ----------------------
    idce    idsn      name  
    khce1   *NULL*    gold
    *NULL*  khsn1     silver         

ความสมบูรณ์จะถูกตรวจสอบก็ต่อเมื่อคอลัมน์ไม่เป็นโมฆะดังนั้นคุณสามารถรักษาความสมบูรณ์ของการอ้างอิงได้


6

ฉันรู้ว่านี่เป็นหัวข้อที่หยุดนิ่งมานาน แต่ในกรณีที่ใครก็ตามค้นหานี่คือวิธีที่ฉันจัดการกับคีย์ต่างประเทศหลายตาราง ด้วยเทคนิคนี้คุณไม่มี DBA บังคับใช้การดำเนินการเรียงซ้อนดังนั้นโปรดตรวจสอบให้แน่ใจว่าคุณจัดการกับสิ่งDELETEนั้นในโค้ดของคุณ

Table 1 Fruit
pk_fruitid, name
1, apple
2, pear

Table 2 Meat
Pk_meatid, name
1, beef
2, chicken

Table 3 Entity's
PK_entityid, anme
1, fruit
2, meat
3, desert

Table 4 Basket (Table using fk_s)
PK_basketid, fk_entityid, pseudo_entityrow
1, 2, 2 (Chicken - entity denotes meat table, pseudokey denotes row in indictaed table)
2, 1, 1 (Apple)
3, 1, 2 (pear)
4, 3, 1 (cheesecake)

ตัวอย่างของ SO Op จะมีลักษณะเช่นนี้

deductions
--------------
type    id      name
1      khce1   gold
2      khsn1   silver

types
---------------------
1 employees_ce
2 employees_sn

1

เป็นไปได้ในทางเทคนิค คุณอาจอ้างถึงพนักงาน _ce ในการหักเงินและพนักงาน _sn แต่ทำไมคุณไม่รวมพนักงาน _sn และพนักงาน _ce ฉันไม่เห็นเหตุผลว่าทำไมคุณถึงมีสองโต๊ะ ไม่มีใครให้ความสัมพันธ์มากมาย และ (ไม่ใช่ในตัวอย่างนี้) หลายคอลัมน์

หากคุณทำการอ้างอิงสองรายการสำหรับหนึ่งคอลัมน์พนักงานต้องมีรายการในทั้งสองตาราง


1

ใช่มันเป็นไปได้ คุณจะต้องกำหนด 2 FK สำหรับตารางที่ 3 FK แต่ละตัวชี้ไปยังฟิลด์ที่ต้องการของหนึ่งตาราง (เช่น 1 FK ต่อตารางต่างประเทศ)


0

สมมติว่าคุณต้องมีสองตารางสำหรับพนักงานทั้งสองประเภทด้วยเหตุผลบางประการฉันจะขยายคำตอบของ vmarquez:

Schema:

employees_ce (id, name)
employees_sn (id, name)
deductions (id, parentId, parentType, name)

ข้อมูลในการหักเงิน:

deductions table
id      parentId      parentType      name
1       1             ce              gold
2       1             sn              silver
3       2             sn              wood
...

วิธีนี้จะช่วยให้คุณมีการหักคะแนนไปยังตารางอื่น ๆ ในสคีมาของคุณ ความสัมพันธ์ประเภทนี้ไม่ได้รับการสนับสนุนโดยข้อ จำกัด ระดับฐานข้อมูล IIRC ดังนั้นคุณจะต้องตรวจสอบให้แน่ใจว่าแอปของคุณจัดการข้อ จำกัด ได้อย่างเหมาะสม (ซึ่งจะทำให้ยุ่งยากมากขึ้นหากคุณมีแอป / บริการที่แตกต่างกันหลายรายการที่กดฐานข้อมูลเดียวกัน)

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