การออกแบบฐานข้อมูล: ความสัมพันธ์ 1 ถึง 2 หลายรายการไปยังตารางเดียวกัน


20

ฉันต้องสร้างแบบจำลองสถานการณ์ที่ฉันมีตาราง Chequing_Account (ซึ่งมีงบประมาณหมายเลข iban และรายละเอียดอื่น ๆ ของบัญชี) ซึ่งจะต้องเกี่ยวข้องกับสองตารางที่แตกต่างกัน Person and Corporation ซึ่งทั้งสองสามารถมีบัญชี chequing 0, 1 หรือหลายบัญชี

กล่าวอีกนัยหนึ่งฉันมีความสัมพันธ์แบบ 1 ต่อ 2 สองความสัมพันธ์กับบัญชี Chequing ตารางเดียวกัน

ฉันต้องการฟังวิธีแก้ไขปัญหานี้ซึ่งเป็นไปตามข้อกำหนดการทำให้เป็นมาตรฐาน โซลูชันส่วนใหญ่ที่ฉันเคยได้ยินคือ:

1) ค้นหาเอนทิตีทั่วไปที่ทั้ง Person และ Corporation อยู่และสร้างตารางเชื่อมโยงระหว่างตารางนี้กับตาราง Chequing_Account สิ่งนี้ไม่สามารถทำได้ในกรณีของฉันและแม้ว่าฉันต้องการแก้ไขปัญหาทั่วไปไม่ใช่อินสแตนซ์เฉพาะนี้

2) สร้างตารางเชื่อมโยงสองตาราง PersonToChequingAccount และ CorporationToChequingAccount ซึ่งเกี่ยวข้องกับสองเอนทิตีกับบัญชี Chequing อย่างไรก็ตามฉันไม่ต้องการให้บุคคลสองคนมีบัญชี chequing เดียวกันและฉันไม่ต้องการให้บุคคลธรรมดาและ บริษัท แบ่งปันบัญชี chequing! เห็นภาพนี้

http://i41.tinypic.com/35i6kbk.png

3) สร้างกุญแจต่างประเทศสองใบในบัญชี Chequing ซึ่งชี้ไปที่ บริษัท และบุคคลธรรมดาอย่างไรก็ตามฉันจะบังคับให้บุคคลและ บริษัท สามารถมีบัญชี chequing จำนวนมาก แต่ฉันจะต้องตรวจสอบให้แน่ใจด้วยตนเองว่าแถว ChequingAccount แต่ละรายการไม่เกี่ยวข้องกัน บริษัท และบุคคลธรรมดาเพราะบัญชี checquing เป็น บริษัท หรือของบุคคลธรรมดา เห็นภาพนี้

http://i40.tinypic.com/1rpv9z.png

มีวิธีอื่นที่สะอาดกว่าสำหรับปัญหานี้หรือไม่?


คุณคิดเกี่ยวกับการมีเช่นOwnerTypeIDในChecquingAccountตารางด้วย1=Corporationและ2=NaturalPerson? วิธีการที่คุณต้องการเพียงหนึ่งOwnerIDในตารางที่คุณสามารถจัดทำดัชนีพร้อมกับChecquingAccount OwnerTypeID
RoKa

ฉันไม่เพียง แต่ต้องรู้ว่ามันเป็น บริษัท หรือบุคคลธรรมดา แต่ต้องรู้รหัสที่เกี่ยวข้องด้วยดังนั้นฉันจึงต้องการหมายเลขประจำตัวและไม่เพียง แต่มูลค่า 1 หรือ 2 เท่านั้น! โซลูชันที่ 3 คือสิ่งที่ฉันพบที่นี่ฉันมีสองคอลัมน์ที่มีรหัส บริษัท หรือบุคคลธรรมดา
dendini

2
ใช่การแก้ปัญหาเป็นตัวเลือกที่ถูกต้อง ใน DBMS ส่วนใหญ่คุณสามารถบังคับใช้ว่าหนึ่งในสอง FKs เท่านั้นคือ "แอ็คทีฟ" ที่มีข้อ จำกัด การตรวจสอบ: CHECK (CorporationID IS NOT NULL AND NaturalPersonID IS NULL OR CorporationID IS NULL AND NaturalPersonID IS NOT NULL)ฉันชอบโซลูชันที่ 1 มากกว่า (แต่นั่นเป็นเพียงฉัน) มันสะอาดกว่ามาก
ypercubeᵀᴹ

ใช่ฉันเข้าใจ แต่คุณอาจมีบันทึกในChecquingAccountตารางOwnerTypeID=1และOwnerID=123ระบุว่าเป็นประเภทCorporationดังนั้น ID 123ในCorporationตาราง OwnerTypeID จะบอกคุณว่าตารางใดและ OwnerID จะบอก ID ให้คุณในตารางนั้น
RoKa

1
ตัวเลือก # 1 เป็นไปไม่ได้อย่างไร คำว่า "บริษัท " นั้นโดยทั่วไปหมายถึง "ธุรกิจที่ถูกต้องตามกฎหมายบุคคล" หลังจากทั้งหมด เรียกว่าCustomersตาราง
จอนแห่งการค้าทั้งหมด

คำตอบ:


15

ฐานข้อมูลเชิงสัมพันธ์ไม่ได้ถูกสร้างขึ้นเพื่อจัดการกับสถานการณ์นี้อย่างสมบูรณ์ คุณต้องตัดสินใจว่าอะไรสำคัญที่สุดสำหรับคุณแล้วทำการแลกเปลี่ยน คุณมีหลายเป้าหมาย:

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

ปัญหาคือว่าบางส่วนของเป้าหมายเหล่านี้แข่งขันกัน

Sub-พิมพ์ดีดโซลูชั่น
คุณสามารถเลือกวิธีการแก้ปัญหาย่อยการพิมพ์ที่คุณสร้างซุปเปอร์ประเภทที่รวมเอาทั้งองค์กรและบุคคล super-type นี้อาจมีคีย์ผสมของคีย์ธรรมชาติของ sub-type พร้อมกับคุณสมบัติการแบ่ง (เช่นcustomer_type) นี่เป็นเรื่องปกติตราบใดที่การฟื้นฟูเป็นไปตามปกติและช่วยให้คุณสามารถบังคับใช้การอ้างอิงที่สมบูรณ์เช่นเดียวกับข้อ จำกัด ที่ บริษัท และบุคคลต่างร่วม ปัญหาคือสิ่งนี้ทำให้การดึงข้อมูลยากขึ้นเนื่องจากคุณต้องแยกสาขาตามcustomer_typeเวลาที่คุณเข้าร่วมบัญชีกับเจ้าของบัญชี นี่อาจหมายถึงการใช้UNIONและมี SQL ซ้ำหลายครั้งในการสืบค้นของคุณ

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

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

โซลูชัน "การแบ่งพาร์ติชันแบบ" ปกติ "
มีอีกตัวเลือกหนึ่งที่คุณเก็บคอลัมน์คีย์ต่างประเทศเดียวในตารางบัญชี chequing และใช้คอลัมน์อื่นเพื่อบอกวิธีตีความคอลัมน์คีย์ต่างประเทศ (RoKa ของOwnerTypeIDคอลัมน์). สิ่งนี้จะกำจัดตาราง super-type เป็นหลักในโซลูชันการพิมพ์ย่อยโดย denormalizing แอตทริบิวต์การแบ่งพาร์ติชันไปยังตารางลูก (โปรดทราบว่านี่ไม่ใช่ "denormalization" อย่างเคร่งครัดตามคำนิยามอย่างเป็นทางการเนื่องจากแอตทริบิวต์การแบ่งเป็นส่วนหนึ่งของคีย์หลัก) วิธีการแก้ปัญหานี้ค่อนข้างง่ายเนื่องจากหลีกเลี่ยงการมีตารางพิเศษเพื่อทำสิ่งเดียวกันหรือมากกว่า ลดจำนวนคอลัมน์สำคัญต่างประเทศลงเหลือหนึ่งคอลัมน์ ปัญหาของการแก้ปัญหานี้คือมันไม่ได้หลีกเลี่ยงการแยกของตรรกะการค้นคืนและยิ่งไปกว่านั้นมันไม่อนุญาตให้คุณรักษาความสมบูรณ์ของการอ้างอิงที่เปิดเผย ฐานข้อมูล SQL ไม่มีความสามารถในการจัดการคอลัมน์คีย์ต่างประเทศเดียวสำหรับหนึ่งในหลายตารางหลัก

โซลูชันโดเมนคีย์หลักที่แชร์
วิธีหนึ่งที่ผู้คนจัดการกับปัญหานี้ในบางครั้งคือใช้ ID กลุ่มเดียวเพื่อไม่ให้สับสนกับ ID ที่ระบุไม่ว่าจะเป็นประเภทย่อยหนึ่งประเภทหรืออีกประเภทหนึ่ง นี่อาจจะทำงานได้อย่างเป็นธรรมชาติในสถานการณ์การธนาคารเนื่องจากคุณจะไม่ออกหมายเลขบัญชีธนาคารเดียวกันให้กับทั้ง บริษัท และบุคคลธรรมดา นี่เป็นข้อได้เปรียบของการหลีกเลี่ยงความต้องการแอตทริบิวต์การแบ่งพาร์ติชัน คุณสามารถทำได้โดยใช้หรือไม่ใช้ตารางเสริม การใช้ตารางชนิดพิเศษอนุญาตให้คุณใช้ข้อ จำกัด ที่เปิดเผยเพื่อบังคับให้มีลักษณะเฉพาะ มิฉะนั้นจะต้องมีการบังคับใช้ขั้นตอนนี้ โซลูชันนี้ได้รับการทำให้เป็นมาตรฐาน แต่จะไม่อนุญาตให้คุณรักษาความสมบูรณ์ของ Referential Referential เว้นแต่ว่าคุณจะเก็บตารางชนิดซุปเปอร์ มันยังไม่ทำอะไรเลยเพื่อหลีกเลี่ยงตรรกะการค้นคืนที่ซับซ้อน

คุณสามารถเห็นได้ว่ามันเป็นไปไม่ได้จริงๆที่จะมีการออกแบบที่สะอาดตามกฎทั้งหมดขณะเดียวกันก็ทำให้การดึงข้อมูลของคุณง่ายขึ้น คุณต้องตัดสินใจว่าการแลกเปลี่ยนของคุณจะเป็นอย่างไร


โซลูชัน # 2 ของคุณอยู่ในกลุ่มใดในสี่กลุ่มของคุณ "การแก้ปัญหาการแบ่งพาร์ติชันแบบ
ธรรมดา

@dendini - โซลูชันของคุณหมายเลข 2 ไม่ตรงกับโซลูชันใด ๆ ที่ฉันระบุ นี่เป็นเพราะมันไม่สอดคล้องกับข้อกำหนดของบัญชีที่เป็นของนิติบุคคลหนึ่ง วิธีที่คุณกำหนดคีย์หลักของตารางกลางพวกมันเป็นจุดตัดหลายต่อหลายกลุ่ม หากปุ่มหลักเป็นเพียง corporation_idและperson_idจากนั้นคุณจะมีวิธีแก้ปัญหาการพิมพ์ย่อยยกเว้นว่าตารางชนิดซุปเปอร์จะถูกแบ่งออกเป็นสองและรหัสต่างประเทศจะได้รับฤvertedษีดังนั้นคนไม่สามารถถือบัญชีหลายบัญชี การเอาชนะจุดประสงค์แบบนี้
Joel Brown

คำอธิบายที่ดี @JoelBrown อะไรคือความหมายของประสิทธิภาพของ "สองคีย์ต่างประเทศ" ในแง่ของการสืบค้น? นอกจากนี้เมื่อพิจารณาว่าแทนที่จะเป็น 2 อาจมีคีย์ต่างประเทศ 6 ตัวขึ้นไป: คุณยังคงแนะนำวิธีนี้หรือไม่ก็เอนไปทางอื่น
Amadeo Gallardo

1
@AdooGallardo คำตอบคือ "มันขึ้นอยู่กับ" การสืบค้นคีย์นั้นค่อนข้างมีประสิทธิภาพอยู่เสมอเนื่องจากโดยทั่วไปคุณสามารถวางใจในการสแกนดัชนีอย่างน้อยถ้าไม่ใช่การค้นหาและสิ่งเหล่านี้เป็นการดำเนินการที่รวดเร็ว ปัญหานี้จะเกิดขึ้นเมื่อคุณค้นหาทั้งสองคีย์ในโซลูชันสองคีย์ต่างประเทศ ที่นี่คุณกำลังขอให้เครื่องมือเพิ่มประสิทธิภาพการสืบค้นทำการ / หรือการดำเนินการ สิ่งที่ดีที่สุดนี้จะทำให้ค่าใช้จ่ายของการสืบค้นเพิ่มขึ้นเป็นสองเท่าโดยปกติจะแย่กว่าเล็กน้อยเนื่องจากคุณต้องทำการค้นหากับคีย์หนึ่งจากนั้นคีย์อื่นจะรวมผลลัพธ์เข้าด้วยกัน
Joel Brown

@JoelBrown เวอร์ชันปกติของ SQL ในอนาคตควรอนุญาตให้ใช้วิธีนี้ได้โดยอนุญาตให้มีการกำหนดคีย์ต่างประเทศแบบผสมตามสองคอลัมน์RefIDและRefTableตำแหน่งที่RefTableเป็นรหัสคงที่ที่ระบุตารางเป้าหมาย มีกรณีการใช้งานมากมายสำหรับคีย์ประเภทนี้และมีจำนวนมากเพื่อรักษาตารางการเชื่อมโยง / ประเภทย่อย 10 รายการขึ้นไปเพื่อบังคับใช้ความสมบูรณ์ สำหรับกรณีเหล่านั้นฉันสร้างขึ้นkeyเอง
djmj
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.