ฉันควรใช้ความสัมพันธ์แบบหนึ่งต่อหนึ่งเมื่อใด


92

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

คำตอบ:


105

1 ถึง 0..1

  • "1 ถึง 0..1" ระหว่างซูเปอร์คลาสและคลาสย่อยถูกใช้เป็นส่วนหนึ่งของกลยุทธ์ "คลาสทั้งหมดในตารางแยกกัน" สำหรับการนำการสืบทอดมาใช้

  • "1 ถึง 0..1" สามารถแสดงในตารางเดียวโดยมีส่วน "0..1" ที่ครอบคลุมด้วยฟิลด์ NULL-able อย่างไรก็ตามหากความสัมพันธ์ส่วนใหญ่เป็น "1 ถึง 0" โดยมีแถว "1 ถึง 1" เพียงไม่กี่แถวการแยกส่วน "0..1" ออกเป็นตารางแยกต่างหากอาจช่วยประหยัดพื้นที่จัดเก็บข้อมูล (และประสิทธิภาพของแคช) ได้ ฐานข้อมูลบางแห่งมีความประหยัดในการจัดเก็บ NULL มากกว่าฐานข้อมูลอื่น ๆ ดังนั้น "จุดตัด" ที่กลยุทธ์นี้ใช้งานได้อาจแตกต่างกันไปมาก

1 ต่อ 1

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

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

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

  • ตารางแยกกันอาจช่วยให้มีการรักษาความปลอดภัยที่ละเอียดยิ่งขึ้น

การพิจารณาเหล่านี้ไม่เกี่ยวข้องในกรณีส่วนใหญ่ดังนั้นในกรณีส่วนใหญ่คุณควรพิจารณารวมตาราง "1 ถึง 1" ไว้ในตารางเดียว


20

หากข้อมูลในตารางหนึ่งเกี่ยวข้อง แต่ไม่ 'เป็นของ' ของเอนทิตีที่อธิบายโดยอีกรายการนั่นคือผู้สมัครที่จะแยกมันออกจากกัน

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


19

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

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

หากตารางของคุณมี 20 แอตทริบิวต์และมีการใช้เพียง 4 รายการในบางครั้งคุณควรแบ่งตารางออกเป็น 2 ตารางสำหรับปัญหาด้านประสิทธิภาพ

ในกรณีเช่นนี้ไม่ควรมีทุกอย่างในตารางเดียว นอกจากนี้มันไม่ใช่เรื่องง่ายที่จะจัดการกับตารางที่มี 45 คอลัมน์!


17

2 เซ็นต์ของฉัน

ฉันทำงานในสถานที่ที่เราทุกคนพัฒนาในแอปพลิเคชันขนาดใหญ่และทุกอย่างเป็นโมดูล ตัวอย่างเช่นเรามีusersตารางและเรามีโมดูลที่เพิ่มรายละเอียด facebook สำหรับผู้ใช้โมดูลอื่นที่เพิ่มรายละเอียด twitter ให้กับผู้ใช้ เราสามารถตัดสินใจที่จะถอดปลั๊กหนึ่งในโมดูลเหล่านั้นและลบฟังก์ชันการทำงานทั้งหมดออกจากแอปพลิเคชันของเรา ในกรณีนี้ทุกโมดูลจะเพิ่มตารางของตนเองโดยมีความสัมพันธ์แบบ 1: 1 ในusersตารางส่วนกลางเช่นนี้

create table users ( id int primary key, ...);
create table users_fbdata ( id int primary key, ..., constraint users foreighn key ...)
create table users_twdata ( id int primary key, ..., constraint users foreighn key ...)

13

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

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

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

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


5

ไม่บ่อยมาก.

คุณอาจพบประโยชน์บางอย่างหากคุณต้องการใช้การรักษาความปลอดภัย - ดังนั้นผู้ใช้บางคนสามารถเห็นบางคอลัมน์ (table1) แต่ไม่เห็นคอลัมน์อื่น (table2) ..

แน่นอนฐานข้อมูลบางอย่าง (Oracle) อนุญาตให้คุณทำการรักษาความปลอดภัยประเภทนี้ในตารางเดียวกัน แต่บางฐานข้อมูลอาจไม่ทำเช่นนั้น


5

คุณกำลังอ้างถึงการทำให้เป็นมาตรฐานฐานข้อมูล ตัวอย่างหนึ่งที่ฉันคิดได้ในแอปพลิเคชันที่ฉันดูแลคือรายการ แอปพลิเคชั่นนี้อนุญาตให้ผู้ใช้ขายสินค้าประเภทต่างๆ (เช่น InventoryItems, NonInventoryItems, ServiceItems ฯลฯ ... ) ในขณะที่ฉันสามารถจัดเก็บเขตข้อมูลทั้งหมดที่ทุกรายการต้องการในตารางรายการเดียวมันง่ายกว่ามากที่จะรักษาตารางรายการพื้นฐานที่มีเขตข้อมูลร่วมกับรายการทั้งหมดจากนั้นแยกตารางสำหรับแต่ละประเภทรายการ (เช่นสินค้าคงคลัง, สินค้าที่ไม่ใช่สินค้าคงคลัง, ฯลฯ .. ) ซึ่งมีฟิลด์เฉพาะสำหรับประเภทรายการนั้นเท่านั้น จากนั้นตารางรายการจะมีคีย์นอกสำหรับประเภทรายการเฉพาะที่แสดง ความสัมพันธ์ระหว่างตารางรายการเฉพาะและตารางรายการพื้นฐานจะเป็นแบบหนึ่งต่อหนึ่ง

ด้านล่างนี้เป็นบทความเกี่ยวกับการทำให้เป็นมาตรฐาน

http://support.microsoft.com/kb/283878


3

เช่นเดียวกับคำถามเกี่ยวกับการออกแบบคำตอบคือ "ขึ้นอยู่กับ"

มีข้อควรพิจารณาเล็กน้อย:

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

  • ช่องในตารางรวมที่มีข้อ จำกัด อาจยุ่งยากในการจัดการเมื่อเวลาผ่านไป ตัวอย่างเช่นหากทริกเกอร์จำเป็นต้องเริ่มทำงานสำหรับฟิลด์ใดฟิลด์หนึ่งการอัปเดตทุกครั้งในตารางจะเกิดขึ้นไม่ว่าฟิลด์นั้นจะได้รับผลกระทบหรือไม่

  • คุณแน่ใจแค่ไหนว่าความสัมพันธ์จะเป็น 1: 1? ดังที่คำถามนี้ชี้ให้เห็นสิ่งต่างๆอาจซับซ้อนได้อย่างรวดเร็ว


3

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


2

ปกติฉันพบความสัมพันธ์แบบ 1: 1 ทั่วไปสองประเภทในทางปฏิบัติ:

  1. ความสัมพันธ์ IS-A หรือที่เรียกว่าความสัมพันธ์แบบซุปเปอร์ไทป์ / ประเภทย่อย นี่คือเมื่อเอนทิตีประเภทหนึ่งเป็นประเภทของเอนทิตีอื่น (EntityA IS A EntityB) ตัวอย่าง:

    • บุคคลที่มีเอนทิตีแยกต่างหากสำหรับนักบัญชีวิศวกรพนักงานขายภายใน บริษัท เดียวกัน
    • เอนทิตีรายการที่มีเอนทิตีแยกต่างหากสำหรับ Widget, RawMaterial, finishedGood ฯลฯ
    • รถเอนทิตีแยกต่างหากสำหรับรถบรรทุกรถเก๋ง ฯลฯ

    ในสถานการณ์เหล่านี้เอนทิตี supertype (เช่น Person, Item หรือ Car) จะมีแอตทริบิวต์ร่วมกันสำหรับประเภทย่อยทั้งหมดและเอนทิตีประเภทย่อยจะมีแอตทริบิวต์ที่ไม่ซ้ำกันสำหรับแต่ละประเภทย่อย คีย์หลักของประเภทย่อยจะเหมือนกับของซูเปอร์ไทป์

  2. ความสัมพันธ์ "เจ้านาย". นี่คือเมื่อบุคคลเป็นเจ้านายหรือผู้จัดการหรือหัวหน้างานที่ไม่ซ้ำกันของหน่วยงาน (แผนก บริษัท ฯลฯ ) เมื่อมีเจ้านายเพียงคนเดียวที่อนุญาตสำหรับหน่วยขององค์กรจะมีความสัมพันธ์แบบ 1: 1 ระหว่างเอนทิตีบุคคลที่แสดงถึงเจ้านายและเอนทิตีหน่วยขององค์กร


1
ฉันชอบตัวอย่างที่สอง คุณสามารถมีเอนทิตี "แผนก" และเอนทิตี "พนักงาน" ได้ ในแผนกหนึ่งคุณมีพนักงานหลายคนและพนักงานสามารถทำงานได้ในแผนกเดียวเท่านั้น นี่คือ 1: n พนักงานสามารถเป็นหัวหน้าแผนกได้เพียงแผนกเดียวและแผนกนั้นมีหัวหน้างานเพียงคนเดียว ดังนั้นคุณจะได้ตารางสองตารางที่เชื่อมต่อด้วยความสัมพันธ์สองแบบ - 1: n และ 1: 1
cezar

2

อันดับแรกฉันคิดว่ามันเป็นคำถามเกี่ยวกับการสร้างแบบจำลองและการกำหนดสิ่งที่ประกอบด้วยเอนทิตีแยกต่างหาก สมมติว่าคุณมีมีเพียงหนึ่งเดียวและครั้งเดียวcustomers addressแน่นอนว่าคุณสามารถใช้ทุกอย่างในตารางเดียวcustomerแต่ถ้าในอนาคตคุณอนุญาตให้เขามีที่อยู่ตั้งแต่ 2 แห่งขึ้นไปคุณจะต้องปรับเปลี่ยนสิ่งนั้นใหม่ (ไม่ใช่ปัญหา แต่ต้องตัดสินใจอย่างมีสติ)

ฉันยังนึกถึงกรณีที่น่าสนใจที่ไม่ได้กล่าวถึงในคำตอบอื่น ๆ ซึ่งการแบ่งตารางอาจเป็นประโยชน์:

ลองนึกภาพอีกครั้งว่าคุณมีcustomersที่อยู่addressแต่ละรายการ แต่คราวนี้เป็นทางเลือกที่จะมีที่อยู่ แน่นอนคุณสามารถใช้งานเป็นNULLคอลัมน์ที่สามารถใช้ได้เช่นZIP,state,street. แต่สมมติว่าหากคุณมีที่อยู่รัฐนั้นไม่ใช่ทางเลือก แต่เป็น ZIP จะสร้างแบบจำลองในตารางเดียวได้อย่างไร? คุณสามารถใช้ข้อ จำกัด บนcustomerตารางได้ แต่จะง่ายกว่ามากในการแบ่งตารางอื่นและทำให้ Foreign_key NULLable ด้วยวิธีนี้แบบจำลองของคุณจะชัดเจนกว่ามากในการบอกว่าเอนทิตี addressเป็นทางเลือกและZIPเป็นแอตทริบิวต์ทางเลือกของเอนทิตีนั้น


0

ในช่วงเวลาของการเขียนโปรแกรมฉันพบสิ่งนี้ในสถานการณ์เดียวเท่านั้น ซึ่งก็คือเมื่อมีความสัมพันธ์แบบ 1 ต่อกลุ่มและ 1 ต่อ 1 ระหว่าง 2 เอนทิตีเดียวกัน ("เอนทิตี A" และ "เอนทิตี B")

เมื่อ "เอนทิตี A" มี "เอนทิตี B" หลายรายการและ "เอนทิตี B" มีเพียง 1 "เอนทิตี A" และ "เอนทิตี A" มีเพียง 1 เอนทิตี B ปัจจุบันและ "เอนทิตีบี" มี "เอนทิตี A" เพียง 1 รายการ

ตัวอย่างเช่นรถยนต์สามารถมีคนขับปัจจุบันได้เพียงคนเดียวและคนขับสามารถขับรถได้ครั้งละหนึ่งคันเท่านั้นดังนั้นความสัมพันธ์ระหว่างแนวคิดของรถยนต์และคนขับจะเป็น 1 ต่อ 1 - ฉันยืมตัวอย่างนี้มาจากคำตอบของ @Steve Fenton

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


0

กรณีการใช้งานอื่นอาจเป็นได้หากเกินจำนวนคอลัมน์สูงสุดในตารางฐานข้อมูล จากนั้นคุณสามารถเข้าร่วมโต๊ะอื่นโดยใช้ OneToOne

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