supertype / ย่อย
วิธีการเกี่ยวกับรูปแบบ supertype / subtype? คอลัมน์ทั่วไปจะอยู่ในตารางหลัก แต่ละประเภทที่แตกต่างมีตารางของตัวเองที่มี ID ของผู้ปกครองเป็นของตัวเองของมันและมันมีคอลัมน์ที่ไม่ซ้ำกันไม่ได้ทั่วไปในทุกประเภทย่อย คุณสามารถรวมคอลัมน์ประเภทในทั้งตารางหลักและตารางรองเพื่อให้แน่ใจว่าแต่ละอุปกรณ์ไม่สามารถมีมากกว่าหนึ่งประเภทย่อย ทำ FK ระหว่างลูกกับพาเรนต์บน (ItemID, ItemTypeID) คุณสามารถใช้ FK เพื่อตาราง supertype หรือ subtype เพื่อรักษาความสมบูรณ์ที่ต้องการในที่อื่น ตัวอย่างเช่นถ้าอนุญาต ItemID ประเภทใด ๆ ให้สร้าง FK ลงในตารางหลัก ถ้าสามารถอ้างอิง SubItemType1 เท่านั้นให้สร้าง FK ลงในตารางนั้น ฉันจะปล่อย TypeID ออกจากตารางอ้างอิง
การตั้งชื่อ
เมื่อพูดถึงการตั้งชื่อคุณมีสองทางเลือกตามที่ฉันเห็น (เนื่องจากตัวเลือกที่สามของ "ID" อยู่ในใจของฉันคือรูปแบบการต่อต้านที่แข็งแกร่ง) เรียกใช้คีย์ย่อย ItemID เหมือนกับที่อยู่ในตารางพาเรนต์หรือเรียกว่าชื่อย่อยเช่น DoohickeyID หลังจากความคิดและประสบการณ์บางอย่างเกี่ยวกับเรื่องนี้ฉันแนะนำให้เรียกมันว่า DoohickeyID สาเหตุของเรื่องนี้คือแม้ว่าจะมีความสับสนเกี่ยวกับตารางย่อยในการปลอมแปลงรายการที่มีรายการ (มากกว่า Doohickeys) ซึ่งเป็นค่าลบเล็กน้อยเมื่อเทียบกับเมื่อคุณสร้าง FK ไปยังตาราง Doohickey และชื่อคอลัมน์ไม่ การจับคู่!
เพื่อ EAV หรือไม่ต่อ EAV - ประสบการณ์ของฉันกับฐานข้อมูล EAV
ถ้า EAV เป็นสิ่งที่คุณต้องทำอย่างแท้จริงนั่นคือสิ่งที่คุณต้องทำ แต่ถ้ามันไม่ใช่สิ่งที่คุณต้องทำล่ะ
ฉันสร้างฐานข้อมูล EAV ที่ใช้งานในธุรกิจ ขอบคุณพระเจ้าชุดข้อมูลมีขนาดเล็ก (แม้ว่าจะมีหลายประเภทรายการ) ดังนั้นประสิทธิภาพจึงไม่เลว แต่มันจะไม่ดีถ้าฐานข้อมูลมีมากกว่าสองสามพันรายการในนั้น! นอกจากนี้ตารางยังยากที่จะสอบถาม ประสบการณ์นี้ทำให้ฉันปรารถนาที่จะหลีกเลี่ยงฐานข้อมูล EAV ในอนาคตหากเป็นไปได้
ตอนนี้ในฐานข้อมูลของฉันฉันสร้างขั้นตอนการจัดเก็บที่สร้างมุมมอง PIVOTed โดยอัตโนมัติสำหรับแต่ละประเภทย่อยที่มีอยู่ ฉันสามารถสอบถามได้จาก AutoDoohickey ข้อมูลเมตาของฉันเกี่ยวกับชนิดย่อยมีคอลัมน์ "ชื่อย่อ" ที่มีชื่อที่ปลอดภัยสำหรับวัตถุที่เหมาะสำหรับใช้ในชื่อมุมมอง ฉันได้ปรับปรุงมุมมอง! น่าเสียดายที่คุณไม่สามารถอัปเดตพวกเขาเมื่อเข้าร่วม แต่คุณสามารถแทรกแถวที่มีอยู่แล้วให้พวกเขาซึ่งจะถูกแปลงเป็น UPDATE น่าเสียดายที่คุณไม่สามารถอัปเดตคอลัมน์เพียงไม่กี่คอลัมน์เนื่องจากไม่มีวิธีที่จะระบุถึงคอลัมน์ที่คุณต้องการอัปเดตด้วยกระบวนการแปลง INSERT-to-UPDATE: ค่า NULL ดูเหมือนว่า "อัปเดตคอลัมน์นี้เป็น NULL" แม้ว่า คุณต้องการระบุว่า "อย่าอัปเดตคอลัมน์นี้เลย"
แม้จะมีการตกแต่งทั้งหมดนี้เพื่อให้ฐานข้อมูล EAV ใช้งานได้ง่ายขึ้น แต่ฉันก็ยังไม่ได้ใช้มุมมองเหล่านี้ในการสืบค้นตามปกติเพราะมันช้า เงื่อนไขการสืบค้นไม่ได้ถูกผลักออกไปตลอดทางValue
ดังนั้นจึงต้องสร้างชุดผลลัพธ์ระดับกลางของรายการทั้งหมดของประเภทมุมมองนั้นก่อนทำการกรอง อุ๊ยตาย ดังนั้นฉันมีหลายแบบสอบถามจำนวนมากที่มีจำนวนมากเข้าร่วมจำนวนมากแต่ละคนออกไปเพื่อให้ได้ค่าที่แตกต่างและอื่น ๆ พวกเขาทำงานได้ค่อนข้างดี แต่อุ๊ย! นี่คือตัวอย่าง SP ที่สร้างสิ่งนี้ (และทริกเกอร์การอัปเดต) เป็นสัตว์ร้ายตัวหนึ่งและฉันภูมิใจในมัน แต่มันไม่ใช่สิ่งที่คุณต้องการจะลองทำ
CREATE VIEW [dbo].[AutoModule]
AS
--This view is automatically generated by the stored procedure AutoViewCreate
SELECT
ElementID,
ElementTypeID,
Convert(nvarchar(160), [3]) [FullName],
Convert(nvarchar(1024), [435]) [Descr],
Convert(nvarchar(255), [439]) [Comment],
Convert(bit, [438]) [MissionCritical],
Convert(int, [464]) [SupportGroup],
Convert(int, [461]) [SupportHours],
Convert(nvarchar(40), [4]) [Ver],
Convert(bit, [28744]) [UsesJava],
Convert(nvarchar(256), [28745]) [JavaVersions],
Convert(bit, [28746]) [UsesIE],
Convert(nvarchar(256), [28747]) [IEVersions],
Convert(bit, [28748]) [UsesAcrobat],
Convert(nvarchar(256), [28749]) [AcrobatVersions],
Convert(bit, [28794]) [UsesDotNet],
Convert(nvarchar(256), [28795]) [DotNetVersions],
Convert(bit, [512]) [WebApplication],
Convert(nvarchar(10), [433]) [IFAbbrev],
Convert(int, [437]) [DataID],
Convert(nvarchar(1000), [463]) [Notes],
Convert(nvarchar(512), [523]) [DataDescription],
Convert(nvarchar(256), [27991]) [SpecialNote],
Convert(bit, [28932]) [Inactive],
Convert(int, [29992]) [PatchTestedBy]
FROM (
SELECT
E.ElementID + 0 ElementID,
E.ElementTypeID,
V.AttrID,
V.Value
FROM
dbo.Element E
LEFT JOIN dbo.Value V ON E.ElementID = V.ElementID
WHERE
EXISTS (
SELECT *
FROM dbo.LayoutUsage L
WHERE
E.ElementTypeID = L.ElementTypeID
AND L.AttrLayoutID = 7
)
) X
PIVOT (
Max(Value)
FOR AttrID IN ([3], [435], [439], [438], [464], [461], [4], [28744], [28745], [28746], [28747], [28748], [28749], [28794], [28795], [512], [433], [437], [463], [523], [27991], [28932], [29992])
) P;
ต่อไปนี้เป็นมุมมองที่สร้างขึ้นโดยอัตโนมัติอีกประเภทที่สร้างขึ้นโดยกระบวนงานที่เก็บไว้อื่นจากข้อมูลเมตาพิเศษเพื่อช่วยค้นหาความสัมพันธ์ระหว่างรายการที่สามารถมีหลายเส้นทางระหว่างกัน (โดยเฉพาะ: โมดูล -> เซิร์ฟเวอร์, โมดูล -> คลัสเตอร์ -> เซิร์ฟเวอร์โมดูล -> DBMS- > เซิร์ฟเวอร์, โมดูล -> DBMS-> Cluster-> เซิร์ฟเวอร์):
CREATE VIEW [dbo].[Link_Module_Server]
AS
-- This view is automatically generated by the stored procedure LinkViewCreate
SELECT
ModuleID = A.ElementID,
ServerID = B.ElementID
FROM
Element A
INNER JOIN Element B
ON EXISTS (
SELECT *
FROM
dbo.Element R1
WHERE
A.ElementID = R1.ElementID1
AND B.ElementID = R1.ElementID2
AND R1.ElementTypeID = 38
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 40
AND B.ElementID = R2.ElementID2
AND R2.ElementTypeID = 38
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 38
AND B.ElementID = R2.ElementID2
AND R2.ElementTypeID = 3122
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
INNER JOIN dbo.Element C2 ON R2.ElementID2 = C2.ElementID
INNER JOIN dbo.Element R3 ON R2.ElementID2 = R3.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 40
AND C2.ElementTypeID = 3080
AND R2.ElementTypeID = 38
AND B.ElementID = R3.ElementID2
AND R3.ElementTypeID = 3122
)
WHERE
A.ElementTypeID = 9
AND B.ElementTypeID = 17
แนวทางไฮบริด
หากคุณต้องมีแง่มุมแบบไดนามิกของฐานข้อมูล EAV บางส่วนคุณสามารถพิจารณาสร้างข้อมูลเมตาได้ราวกับว่าคุณมีฐานข้อมูลดังกล่าว แต่จริงๆแล้วใช้รูปแบบการออกแบบ supertype / subtype แทน ใช่คุณจะต้องสร้างตารางใหม่และเพิ่มและลบและแก้ไขคอลัมน์ แต่ด้วยการประมวลผลล่วงหน้าที่เหมาะสม (อย่างที่ฉันทำกับมุมมองอัตโนมัติของฐานข้อมูล EAV ของฉัน) คุณอาจมีวัตถุคล้ายโต๊ะจริง ๆ ใช้งานได้ มีเพียงพวกเขาจะไม่เป็น gnarly เหมือนของฉันและเครื่องมือเพิ่มประสิทธิภาพการค้นหาสามารถทำนายการกดลงไปที่ตารางฐาน (อ่าน: ทำงานได้ดีกับพวกเขา) จะมีเพียงหนึ่งการรวมกันระหว่างตาราง supertype และตารางย่อย แอปพลิเคชันของคุณสามารถตั้งค่าให้อ่านข้อมูลเมตาเพื่อค้นหาสิ่งที่ควรทำ (หรือสามารถใช้มุมมองที่สร้างขึ้นอัตโนมัติในบางกรณี)
หรือหากคุณมีชุดย่อยหลายระดับให้เข้าร่วมเพียงไม่กี่ครั้ง โดยหลายระดับฉันหมายถึงเมื่อบางชนิดย่อยใช้คอลัมน์ร่วมกัน แต่ไม่ใช่ทั้งหมดคุณอาจมีตารางย่อยสำหรับประเภทที่เป็น supertype ของตารางอื่นสองสามตัว ตัวอย่างเช่นหากคุณจัดเก็บข้อมูลเกี่ยวกับเซิร์ฟเวอร์เราเตอร์และเครื่องพิมพ์ประเภทย่อยขั้นกลางของ "อุปกรณ์ IP" อาจสมเหตุสมผล
ฉันจะให้ข้อแม้ที่ฉันยังไม่ได้สร้างฐานข้อมูลที่ตกแต่งด้วย EAV แบบ metatable ที่มีรูปแบบ supertype / subtype แบบ EAV อย่างที่ฉันแนะนำที่นี่และยังลองในโลกแห่งความจริง แต่ปัญหาที่ฉันเคยประสบกับ EAV นั้นไม่ใช่เรื่องเล็กและการทำบางสิ่งบางอย่างอาจเป็นสิ่งที่จำเป็นอย่างยิ่งหากฐานข้อมูลของคุณจะใหญ่และคุณต้องการประสิทธิภาพที่ดีโดยไม่ต้องใช้ฮาร์ดแวร์ขนาดยักษ์ราคาแพง
ในความเห็นของฉันเวลาที่ใช้โดยอัตโนมัติการใช้ / การสร้าง / การดัดแปลงของตารางย่อยจริงจะดีที่สุด การมุ่งเน้นไปที่ความยืดหยุ่นที่ได้จากข้อมูลทำให้เสียง EAV นั้นน่าสนใจ (และเชื่อฉันฉันชอบว่าเมื่อมีคนถามฉันถึงคุณลักษณะใหม่ในประเภทองค์ประกอบที่ฉันสามารถเพิ่มได้ในเวลาประมาณ 18 วินาทีและพวกเขาสามารถเริ่มป้อนข้อมูลในเว็บไซต์ได้ทันที ) แต่ความยืดหยุ่นสามารถทำได้มากกว่าหนึ่งวิธี! การประมวลผลล่วงหน้าเป็นอีกวิธีในการดำเนินการ มันเป็นวิธีการที่ทรงพลังซึ่งมีผู้ใช้น้อยคนที่ให้ประโยชน์ในการขับเคลื่อนข้อมูลโดยสิ้นเชิง แต่ประสิทธิภาพของการเขียนโค้ดยาก
(หมายเหตุ: ใช่มุมมองเหล่านั้นถูกจัดรูปแบบเช่นนั้นและมุมมอง PIVOT มีตัวกระตุ้นการอัปเดตจริง ๆ :) หากมีคนสนใจรายละเอียดอันเจ็บปวดของตัวเรียก UPDATE ที่ยาวและซับซ้อนแจ้งให้เราทราบและฉันจะโพสต์ ตัวอย่างสำหรับคุณ)
และอีกหนึ่งความคิด
ใส่ข้อมูลทั้งหมดของคุณไว้ในตารางเดียว ตั้งชื่อคอลัมน์ทั่วไปจากนั้นนำมาใช้ซ้ำ / ไม่เหมาะสมเพื่อวัตถุประสงค์หลายอย่าง สร้างมุมมองเหล่านี้เพื่อให้ชื่อที่สมเหตุสมผล เพิ่มคอลัมน์เมื่อคอลัมน์ที่ไม่ได้ใช้ชนิดข้อมูลที่เหมาะสมไม่พร้อมใช้งานและอัปเดตมุมมองของคุณ แม้ความยาวของฉันจะเกิดขึ้นกับชนิดย่อย / supertype แต่นี่อาจเป็นวิธีที่ดีที่สุด