จำเป็นต้องใช้คอลัมน์ ID ที่ไม่ซ้ำกันในตารางหลายต่อหลาย (ทางแยก) หรือไม่


22

การเริ่มโครงการเล็ก ๆ กับ EF แต่ฉันมีคำถามบางอย่างเกี่ยวกับการเข้าร่วมตารางและกุญแจเป็นต้นให้บอกว่าฉันมีตารางแอปพลิเคชันและตารางสิทธิ์ แอปพลิเคชันมีสิทธิ์มากมายและสิทธิ์แต่ละรายการสามารถเป็นของแอปพลิเคชันจำนวนมาก (หลายต่อหลายคน)

ตอนนี้ตารางใบสมัครและสิทธิ์เป็นเรื่องง่าย:

Applications
--------------
PK  ApplicationID
    Name

Permissions
--------------
PK  PermissionID
    Name

แต่วิธีที่ดีที่สุดที่จะทำตารางเข้าร่วมคืออะไร? ฉันมีสองตัวเลือกเหล่านี้:

ApplicationPermissions
-----------------------
PK  ApplicationPermissionID
CU  ApplicationID
CU  PermissionID

หรือ

ApplicationPermissions
-----------------------
CPK ApplicationID
CPK PermissionID

PK = Primary Key
CPK = Composite Primary Key
CU = Composite Unique Index

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

คำตอบ:


20

ฉันเชื่อว่าคุณหมายถึงตาราง "ทางแยก" ไม่ใช่ตาราง "เข้าร่วม"

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

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

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

หากกรอบงานของคุณต้องการให้ตารางทั้งหมดมี ID ให้ไปหา หากมาตรฐานฐานข้อมูลของทีมของคุณกำหนดตารางทั้งหมดจะต้องมี ID ให้ดำเนินการต่อ ถ้าไม่เช่นนั้นให้หลีกเลี่ยง


2
คุณได้ระบุไว้แล้วว่าการเพิ่ม ID เป็นสัมปทานเล็กน้อยเอาชนะได้ง่ายด้วยผลประโยชน์ที่อาจเกิดขึ้นดังนั้นสำหรับฉันแล้ว (ที่ระบุว่าการมี ID เฉพาะในทุกตารางนั้นเป็นวิธีปฏิบัติที่ดีที่สุดใน DBMS และ ORM ส่วนใหญ่) คุณจะแนะนำให้มีรหัสเป็นตัวเลือก "ดีที่สุด" หรือ "เริ่มต้น" แทนที่จะไม่มี
Robert Harvey

4
"คุณไม่จำเป็นต้องเข้าร่วมหรือสอบถามเกี่ยวกับรหัสดังกล่าว" - การพูดว่า "ไม่เคย" ในสถานการณ์ทางเทคโนโลยีคือการเชิญให้สิ่งนั้นเกิดขึ้น บอกว่ามีอยู่ครั้งเมื่อคุณจะเข้าร่วมที่เข้าร่วมโต๊ะ (ใช่ฉันเคยได้ยินมันเรียกว่า "เข้าร่วม" ตารางมากกว่า "สนธิ" ตาราง) ไปยังโต๊ะที่สี่เพราะหน่วยงานที่เข้าร่วมอยู่ในความเป็นจริง วัตถุธุรกิจของตัวเอง
เจสซีซี. เครื่องตัด

4
@RobertHarvey ID เป็นแนวปฏิบัติที่ดีสำหรับเอนทิตี แต่ทางแยกเป็นรายละเอียดการนำไปใช้งานสำหรับความสัมพันธ์จำนวนมากไม่ใช่เอนทิตีในสิทธิ์ของตนเอง แต่เมื่อตัวเลื่อน Jesse C. ชี้ว่ามีบางกรณีที่มีทางแยกที่อาจถูกพิจารณาว่าเป็นองค์กรธุรกิจ
mike30

1
"เปลืองเนื้อที่ดิสก์" - ฉันคิดว่าเอ็นจิ้นบางตัว (InnoDB?) สร้างคีย์หลัก (ภายใน) ต่อไปถ้าคุณไม่สร้างด้วยตัวเอง - ดังนั้นคุณอาจไม่ได้รับพื้นที่ว่างในดิสก์โดยไม่มี
Alex

@ Alex คุณใส่ PK คอมโพสิตในรหัสที่แมป
mike30

11

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

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

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

และสุดท้ายคำสั่งเช่นSELECT ... FROM TABLE WHERE TableNameID IN (id1,id2,...)นั้นทำงานได้ดีกับคอลัมน์เดียวเป็นคีย์หลัก แต่ฉันไม่เคยเห็นภาษา SQL มาก่อนซึ่งช่วยให้คุณทำสิ่งนี้ได้ด้วยคีย์ผสม ถ้าคุณรู้ล่วงหน้าว่าคุณจะไม่ต้องมีแบบสอบถามแบบนี้เลย แต่ไม่ต้องแปลกใจถ้าพรุ่งนี้คุณจะได้รับข้อกำหนดซึ่งจะแก้ไขได้ง่ายที่สุดด้วย SQL ชนิดนี้

แน่นอนว่าเมื่อคุณคาดหวังของตารางที่จะถือหลายร้อยล้านแถวแล้วคุณควรพิจารณาที่จะหลีกเลี่ยงสิ่งที่ต้องการApplicationPermissionsApplicationPermissionsID


แม้ว่าฉันจะไม่ได้เลือกคำตอบของคุณ ฉันชอบแง่มุมของมัน ขอบคุณสำหรับความคิดของคุณ (upvote)
solidau

6

ในขณะที่คำตอบของ Mike ดีดีนี่คือเหตุผลที่ฉันจะเพิ่มช่อง ID แยกต่างหากหรือไม่

  1. พิจารณาใช้ช่องรหัสแยกต่างหากสำหรับทางแยก / เข้าร่วมโต๊ะถ้ามีสาขาอื่น ๆ กว่าประชาชน สิ่งนี้มีแนวโน้มที่จะทราบว่ามันเป็นเอนทิตีชั้นหนึ่ง

  2. พิจารณาใช้ฟิลด์ ID แยกหาก API หรือตรรกะที่มีอยู่มีแนวโน้มที่จะใช้ฟิลด์เดียวสำหรับการดึง / แก้ไขเอนทิตี ที่สามารถช่วยให้ผู้อื่นติดตามรหัสของคุณในบริบทของโครงการขนาดใหญ่

  3. อย่าใช้มันหากไม่มีประโยชน์เฉพาะ (KISS) EF รู้วิธีจัดการกับตารางประเภทนี้และบางครั้งอาจมีข้อ จำกัด ที่ไม่เหมือนใครเมื่อคนอื่นพยายามเข้าใจความสัมพันธ์ประเภทนี้ นอกจากนี้เมื่อ normalizing ฉันพยายามที่จะใช้ที่เป็นไปได้ที่สำคัญน้อยที่สุดที่ไม่ซ้ำกันกำหนด tuple ในตัวอย่างที่สองของคุณคุณมีคีย์หลักที่แยกจากกันได้อย่างมีประสิทธิภาพ 2 คีย์


-5
table Person
   Id int identity(1,1) not null primary key
   ...other fields go here...
table Address
   Id int identity(1,1) not null primary key
   ...other fields go here...
table PersonAddress
   Id int identity(1,1) not null primary key
   PersonId int not null
   AddressId int not null

โปรดจำไว้ว่าการสร้างดัชนีและต่างประเทศที่สำคัญทั้งในและPersonIdAddressId

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


1
ฉันคิดว่าปัญหาหนึ่งกับวิธีนี้คือ schema อนุญาตให้มีสองPersonAddressแถวที่เหมือนกันPersonIdและมีAddressIdค่า
Sam
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.