SQL - คีย์หลักของตารางหลายต่อหลายตาราง


125

คำถามนี้เกิดขึ้นหลังจากอ่านความคิดเห็นในคำถามนี้:

การออกแบบฐานข้อมูล

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

โดยทั่วไปสิ่งนี้:

PartDevice
----------
PartID (PK/FK)
DeviceID (PK/FK)

เทียบกับสิ่งนี้:

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)

ผู้แสดงความคิดเห็นกล่าวว่า:

การทำให้สอง ID เป็น PK หมายถึงตารางถูกเรียงลำดับทางกายภาพบนดิสก์ตามลำดับนั้น ดังนั้นหากเราแทรก (Part1 / Device1), (Part1 / Device2), (Part2 / Device3) ดังนั้น (Part 1 / Device3) ฐานข้อมูลจะต้องแยกตารางออกจากกันและแทรกรายการสุดท้ายระหว่างรายการ 2 และ 3 สำหรับ หลาย ๆ ระเบียนสิ่งนี้จะกลายเป็นปัญหาอย่างมากเนื่องจากเกี่ยวข้องกับการสับหลายร้อยหลายพันหรือหลายล้านระเบียนทุกครั้งที่มีการเพิ่ม ในทางตรงกันข้าม PK ที่สร้างขึ้นโดยอัตโนมัติช่วยให้สามารถบันทึกข้อมูลใหม่ได้จนจบ

เหตุผลที่ฉันถามเป็นเพราะฉันมักจะทำคีย์หลักแบบผสมโดยไม่มีคอลัมน์การเพิ่มอัตโนมัติตัวแทน แต่ฉันไม่แน่ใจว่าคีย์ตัวแทนมีประสิทธิภาพมากกว่าหรือไม่


นี่เป็นคำถามที่โพสต์บน SO: stackoverflow.com/questions/344068/…
Tony

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

1
คำตอบนี้ไม่ขึ้นอยู่กับว่า DBMS ใช้อะไร? ฉันสงสัยว่า MySQL จะทำงานในลักษณะในกรณีนี้ SQL-Server เล็กน้อยในทางอื่นเป็นต้น
Radu Murzea

ข้อแม้: หากไม่มีแท็กฐานข้อมูลที่เฉพาะเจาะจงสิ่งที่กล่าวมาส่วนใหญ่เป็นสิ่งที่น่าสงสัย เครื่องยนต์ต่างกันทำงานไม่เหมือนกัน!
Rick James

คำตอบ:


85

ด้วยการทำแผนที่แบบหลายต่อหลายคอลัมน์สองคอลัมน์อย่างง่ายฉันไม่เห็นข้อได้เปรียบที่แท้จริงของการมีคีย์ตัวแทน การมีคีย์หลัก(col1,col2)จะรับประกันได้ว่าไม่ซ้ำกัน (สมมติว่าค่าของคุณcol1และcol2ในตารางอ้างอิงไม่ซ้ำกัน) และดัชนีแยกต่างหาก(col2,col1)จะจับกรณีเหล่านั้นซึ่งคำสั่งตรงกันข้ามจะทำงานได้เร็วขึ้น การตั้งครรภ์แทนเป็นการเสียพื้นที่

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

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

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

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


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

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

Think Amazon - คำสั่งซื้อนับพันที่สร้างขึ้นทุกชั่วโมง
ผู้ใช้

9
@buffer ใช่ แต่อีกครั้งคำสั่งซื้อแต่ละรายการมักจะถูกสอบถามหลายครั้งเพื่อทำ (เช่น) บรรจุภัณฑ์การเรียกเก็บเงินการอัปเดตสถานะการวิเคราะห์ธุรกิจและอื่น ๆ จำนวนการสร้างสัมบูรณ์มีความสำคัญน้อยกว่าอัตราส่วนระหว่างการสร้างและการอ่าน
paxdiablo

1
ประเด็นของฉันคือinsertจะสำคัญว่ามันถูกทำหลายพันครั้งต่อชั่วโมง คุณไม่สามารถเพิกเฉยได้เพียงเพราะอัตราส่วนของinsertต่อselectคือ <1 ในกรณีนี้ลูกค้าจะสนใจว่าจะต้องใช้เวลาในการสั่งซื้อเท่าใด
ผู้ใช้

19

ไม่จำเป็นต้องใช้รหัสตัวแทนสำหรับตารางลิงก์

หนึ่ง PK บน (col1, col2) และดัชนีเฉพาะบน (col2, col1) คือสิ่งที่คุณต้องการ

เว้นแต่คุณจะใช้ ORM ที่ไม่สามารถรับมือและกำหนดการออกแบบฐานข้อมูลให้คุณได้ ...

แก้ไข: ฉันตอบเหมือนกันที่นี่: SQL: คุณต้องการคีย์หลักที่เพิ่มขึ้นโดยอัตโนมัติสำหรับตาราง Many-Many หรือไม่?


3
คุณอาจจะพอใจกับดัชนี dups บน col2 แทนที่จะเป็นดัชนีเฉพาะบน (col2, col1) ข้อดีของดัชนีสองคอลัมน์คืออนุญาตให้สแกนดัชนีอย่างเดียวบน col2 เพียงอย่างเดียวหรือทั้ง col1 และ col2 (แม้ว่าดัชนีอื่น ๆ บน (col1, col2) จะจัดการกรณี 'ทั้งสอง' ด้วยเช่นกัน) ข้อเสียคือพื้นที่จัดเก็บเพิ่มเติมที่จำเป็นสำหรับคอลัมน์พิเศษ สิ่งนี้มักไม่สำคัญดังนั้นคำแนะนำจึงห่างไกลจากสิ่งที่น่ากลัว อย่างไรก็ตามหาก col1 และ col2 มีขนาดใหญ่หรือมีขนาดแตกต่างกันมากคุณสามารถประหยัดพื้นที่ของตัวเองได้โดยไม่ต้องเสียประสิทธิภาพโดยเลือกให้ดัชนีที่สองในคอลัมน์ที่สั้นกว่า
Jonathan Leffler

@gbn: ดัชนีที่สองบน (col2, col1) ไม่จำเป็นต้องไม่ซ้ำกันใช่ไหม
ผู้ใช้

1
การใส่ดัชนีที่ไม่ซ้ำกันบน (col1, col2) หลังจากที่มันเป็น PK แล้วจะซ้ำซ้อนทั้งหมด
Don Cheadle

@mmcrae: เรากำลังทำอะไรอยู่?
gbn

2
@mmcrae: ความคิดเห็นของคุณคือ "ใส่ดัชนีเฉพาะ (col1, col2) .. " ลำดับคอลัมน์ในดัชนีมีความสำคัญ ไม่ใช่(col2, col1) (col1, col2)PK ของ(col1, col2)อาจไม่เหมาะสำหรับการค้นหาทั้งหมดและสร้างการสแกนดังนั้นการมีสิ่งที่ตรงกันข้ามจะช่วยเพิ่มประสิทธิภาพเนื่องจากช่วยให้ค้นหาว่า col2 ดีกว่า ตัวอย่างเช่นการตรวจสอบ FK เมื่อตารางที่มี col2 มีการลบ ตรวจสอบโต๊ะเด็ก
gbn

12

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

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

PartDevice
----------
ID (PK/auto-increment)
PartID (FK)
DeviceID (FK)
Other Details

ง่ายต่อการดึง 'รายละเอียดอื่น ๆ ' โดยใช้ PartDevice.ID เป็น FK ดังนั้นจึงจำเป็นต้องใช้คีย์หลักแบบเพิ่มหน่วย


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

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

6

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


2

ดังนั้นดูเหมือนว่าถ้างานเดียวคือการเชื่อมโยงสองตาราง PK ที่ดีที่สุดคือ PK แบบเสาคู่

แต่ถ้าใช้เพื่อวัตถุประสงค์อื่นให้เพิ่ม NDX อีกตัวเป็น PK ด้วยคีย์ต่างประเทศและดัชนีที่ไม่ซ้ำกันที่สอง

Index หรือ PK เป็นวิธีที่ดีที่สุดเพื่อให้แน่ใจว่าไม่มีรายการซ้ำกัน PK ให้เครื่องมืออย่าง Microsoft Management Studio ทำงานบางอย่าง (สร้างมุมมอง) ให้คุณ

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