ตารางอ้างอิงตนเองดีหรือไม่ดี [ปิด]


37

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

ตารางหนึ่งที่มีคอลัมน์ parent_id อ้างอิงตนเอง uk - london (london parent id = UK id)

หรือสองตารางที่มีความสัมพันธ์แบบหนึ่งต่อหลายโดยใช้คีย์ต่างประเทศ

การตั้งค่าของฉันสำหรับตารางอ้างอิงตัวเองหนึ่งตารางเนื่องจากช่วยให้ขยายขอบเขตได้ง่ายตามต้องการ

โดยทั่วไปแล้วคนออกไปจากตารางอ้างอิงตนเองหรือว่าพวกเขา A-OK?

คำตอบ:


40

ไม่มีอะไรผิดปกติกับตารางการอ้างอิงตนเอง

มันเป็นรูปแบบการออกแบบฐานข้อมูลทั่วไปสำหรับลำดับชั้นซ้อนกัน (อินฟินิตี้?)


@NimChimpsky - เช่นเดียวกับแนวคิดของการเรียกซ้ำความคิดนี้ยากสำหรับบางคน
Oded

2
(อย่างน้อย) Oracle ยังมีโครงสร้าง SQL พิเศษอยู่ประโยค "เริ่มต้นด้วย - เชื่อมต่อตาม" เพื่อจัดการกับตารางอ้างอิงตนเอง
281377

1
@ user281377 - และ SQL Server แนะนำhierarchyidประเภท
Oded

usign hibernate ดังนั้นจะมีซอสพิเศษเป็นของตัวเอง
NimChimpsky

4
@NimChimpsky - พิจารณาดู "Nested Set Model" เป็นทางเลือกแทนคอลัมน์ "parent_id" เช่นกัน - มันมีฟังก์ชั่นเดียวกัน แต่ประสิทธิภาพที่ดีขึ้นและแบบสอบถามที่ง่ายกว่าในการแยกลำดับชั้น en.wikipedia.org/wiki/Nested_set_model ชุดหนังสือของ Joe Celko "SQL For Smarties" มีตัวอย่าง SQL ที่ยอดเยี่ยมเกี่ยวกับชุดซ้อน
Keith Palmer Jr.

7

Necromancing
คำตอบที่ถูกต้องคือขึ้นอยู่กับเอ็นจิ้นฐานข้อมูลและเครื่องมือการจัดการใด

ลองทำตัวอย่าง:
เรามีตารางรายงาน
และรายงานสามารถมีพาเรนต์ (Menupoint เช่นหมวดหมู่)
และผู้ปกครองนั้นสามารถมีผู้ปกครองได้ (เช่นศูนย์กำไร)
และอื่น ๆ เพื่อโฆษณา

ตัวอย่างที่ง่ายที่สุดของความสัมพันธ์แบบเรียกซ้ำมาตรฐานเช่นเดียวกับเอนทิตี / ลำดับชั้นที่อ้างอิงตัวเอง

ตาราง SQL-Server ที่ได้คือ:

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_T_FMS_Reports_T_FMS_Reports') AND parent_object_id = OBJECT_ID(N'dbo.T_FMS_Reports'))
ALTER TABLE dbo.T_FMS_Reports DROP CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.T_FMS_Reports') AND type in (N'U'))
DROP TABLE dbo.T_FMS_Reports 
GO



CREATE TABLE dbo.T_FMS_Reports 
( 
     RE_UID uniqueidentifier NOT NULL 
    ,RE_RE_UID uniqueidentifier NULL 
    ,RE_Text nvarchar(255) NULL 
    ,RE_Link nvarchar(400) NULL 
    ,RE_Sort int NOT NULL 
    ,RE_Status int NOT NULL 
    ,PRIMARY KEY CLUSTERED ( RE_UID ) 
); 

GO

ALTER TABLE dbo.T_FMS_Reports  WITH CHECK ADD  CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports FOREIGN KEY(RE_RE_UID) 
REFERENCES dbo.T_FMS_Reports (RE_UID) 
-- ON DELETE CASCADE -- here, MS-SQL has a problem 
GO

ALTER TABLE dbo.T_FMS_Reports CHECK CONSTRAINT FK_T_FMS_Reports_T_FMS_Reports 
GO

แต่คุณได้รับปัญหา:
เมื่อคุณต้องการลบ Menupoint ที่มีเมนูย่อยทั้งหมดคุณไม่สามารถตั้งค่า delete-cascade ได้เนื่องจากMicrosoft SQL-Serverไม่รองรับการลบแบบเรียกซ้ำ (ในทางกลับกัน PostGreSQL จะทำ แต่ถ้า กราฟไม่ได้เป็นวัฏจักร] ในขณะที่ MySQL ไม่ชอบโครงสร้างตารางแบบนี้เลยเพราะมันไม่รองรับ CTE แบบเรียกซ้ำ)

ดังนั้นคุณต้องใช้ความสามารถในการลบ / ความสมบูรณ์ในการทำงานด้วยจึงทำให้จำเป็นต้องใช้ฟังก์ชันดังกล่าวในรหัสของคุณเองหรือในขั้นตอนการจัดเก็บ (หาก RDBMS ของคุณรองรับขั้นตอนการจัดเก็บ)

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

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

อย่างไรก็ตามในระบบการจัดการฐานข้อมูลที่เหมาะสม (เช่น PostgreSQL) ด้วยการใช้เครื่องมือที่เหมาะสมสิ่งนี้ไม่ควรเป็นปัญหา เพียงแค่แยกแยะว่าเพราะคุณจ่าย RDBMS ให้มาก (ฉันกำลังมองหาคุณ Microsoft; Oracle =?) และ / หรือแถบเครื่องมือมันไม่ได้หมายความว่ามันถูกตั้งโปรแกรมอย่างถูกต้อง และ OpenSource (เช่น MySQL) ก็ไม่ได้ทำให้คุณมีภูมิคุ้มกันต่อมินิเทียที่ยอดเยี่ยมเช่นนั้น

ปีศาจอยู่ในรายละเอียดตามที่พูดเก่าไป

ตอนนี้ไม่ใช่ว่าคุณไม่สามารถแก้ไขปัญหาดังกล่าวได้ แต่ฉันจะไม่แนะนำถ้าระบบของคุณจะซับซ้อน (เช่น 200+ ตาราง)
นอกจากนี้ในการตั้งค่าเชิงพาณิชย์ตามปกติ (ตามภาพโดย Dilbert) คุณจะไม่ได้รับเวลานั้น

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


3
+1 เมื่อนำตารางการปิดมาให้ความสนใจของฉัน (อย่างน้อยคำศัพท์รู้แนวคิดแล้ว) นี่เป็นบทความที่ดีสำหรับผู้อื่นที่อาจสนใจ coderwall.com/p/lixing/closure-tables-for-browsing-trees-in-sql
Outfast Source

1

เป็นความคิดที่ดีถ้าความสัมพันธ์นั้นเป็นแบบลำดับชั้นและไม่ใช่ความสัมพันธ์เครือข่าย (ตัวอย่างเช่น Bill of Materials เป็นความสัมพันธ์เครือข่ายไม่ใช่ลำดับชั้น)

สามารถสืบค้นได้ช้า เพื่อเพิ่มความเร็วของสิ่งต่างๆคุณสามารถใช้ตารางการปิดได้

http://karwin.blogspot.ca/2010/03/rendering-trees-with-closure-tables.html

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