Supertype / Subtype
Bagaimana kalau melihat pola supertipe / subtipe? Kolom umum masuk dalam tabel induk. Setiap tipe berbeda memiliki tabelnya sendiri dengan ID induk sebagai PK-nya sendiri dan berisi kolom unik yang tidak umum untuk semua subtipe. Anda bisa memasukkan kolom tipe dalam tabel induk dan anak-anak untuk memastikan setiap perangkat tidak boleh lebih dari satu subtipe. Buat FK antara anak-anak dan orang tua di (ItemID, ItemTypeID). Anda bisa menggunakan FK untuk tabel supertype atau subtype untuk menjaga integritas yang diinginkan di tempat lain. Misalnya, jika ItemID jenis apa pun diizinkan, buat FK ke tabel induk. Jika hanya SubItemType1 yang dapat direferensikan, buat FK ke tabel itu. Saya akan meninggalkan TypeID dari tabel referensi.
Penamaan
Ketika berbicara soal penamaan, Anda memiliki dua pilihan seperti yang saya lihat (karena pilihan ketiga "ID" saja dalam benak saya adalah anti-pola yang kuat). Panggil kunci subtipe ItemID seperti di tabel induk, atau sebut saja nama subtipe seperti DoohickeyID. Setelah beberapa pemikiran dan beberapa pengalaman dengan ini, saya menganjurkan menyebutnya DoohickeyID. Alasan untuk ini adalah bahwa meskipun mungkin ada kebingungan tentang tabel subtipe benar-benar menyamar yang mengandung Item (bukan Doohickeys), itu adalah negatif kecil dibandingkan dengan ketika Anda membuat FK ke tabel Doohickey dan nama kolom tidak pertandingan!
Ke EAV atau tidak ke EAV - Pengalaman saya dengan basis data EAV
Jika EAV adalah apa yang benar-benar harus Anda lakukan, maka itulah yang harus Anda lakukan. Tetapi bagaimana jika itu bukan apa yang harus Anda lakukan?
Saya membangun basis data EAV yang digunakan dalam bisnis. Alhamdulillah, set datanya kecil (meskipun ada lusinan jenis item) sehingga kinerjanya tidak buruk. Tetapi akan buruk jika database memiliki lebih dari beberapa ribu item di dalamnya! Selain itu, tabelnya begitu SULIT untuk kueri. Pengalaman ini telah membuat saya benar-benar ingin menghindari basis data EAV di masa depan jika memungkinkan.
Sekarang, di basis data saya, saya membuat prosedur tersimpan yang secara otomatis membangun tampilan PIVOT untuk setiap subtipe yang ada. Saya bisa saja meminta dari AutoDoohickey. Metadata saya tentang subtipe memiliki kolom "ShortName" yang berisi nama objek-aman yang cocok untuk digunakan dalam nama tampilan. Saya bahkan membuat tampilan dapat diperbarui! Sayangnya, Anda tidak dapat memperbaruinya saat bergabung, tetapi Anda BISA memasukkannya ke baris yang sudah ada, yang akan dikonversi menjadi UPDATE. Sayangnya, Anda tidak dapat memperbarui hanya beberapa kolom, karena tidak ada cara untuk menunjukkan ke VIEW kolom mana yang ingin Anda perbarui dengan proses konversi INSERT-to-UPDATE: nilai NULL terlihat seperti "perbarui kolom ini ke NULL" walaupun Anda ingin menunjukkan "Jangan perbarui kolom ini sama sekali."
Terlepas dari semua dekorasi ini untuk membuat basis data EAV lebih mudah digunakan, saya masih tidak menggunakan pandangan ini dalam kueri paling normal karena ini adalah PERLAHAN. Kondisi kueri bukanlah predikat yang didorong sepenuhnya kembali ke Value
tabel, sehingga harus membuat kumpulan hasil antara semua item dari jenis tampilan itu sebelum memfilter. Aduh. Jadi saya punya banyak, banyak pertanyaan dengan banyak, banyak gabungan, masing-masing keluar untuk mendapatkan nilai yang berbeda dan seterusnya. Mereka tampil relatif baik, tetapi aduh! Ini sebuah contoh. SP yang menciptakan ini (dan pemicu pembaruannya) adalah salah satu binatang buas raksasa, dan saya bangga akan hal itu, tetapi ini bukan sesuatu yang ingin Anda coba pertahankan.
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;
Berikut jenis lain dari tampilan yang dibuat secara otomatis yang dibuat oleh prosedur tersimpan lain dari metadata khusus untuk membantu menemukan hubungan antara item yang dapat memiliki beberapa jalur di antara mereka (Khususnya: Module-> Server, Module-> Cluster-> Server, Module-> DBMS- > Server, Modul-> DBMS-> Cluster-> Server):
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
Pendekatan Hibrida
Jika Anda HARUS memiliki beberapa aspek dinamis dari basis data EAV, Anda dapat mempertimbangkan membuat metadata seolah-olah Anda memiliki basis data seperti itu, tetapi sebaliknya benar-benar menggunakan pola desain supertype / subtipe. Ya, Anda harus membuat tabel baru, dan menambah dan menghapus dan memodifikasi kolom. Tetapi dengan pra-pemrosesan yang tepat (seperti yang saya lakukan dengan tampilan otomatis database EAV saya) Anda bisa memiliki objek seperti tabel nyata untuk bekerja dengannya. Hanya saja, mereka tidak akan sebesar saya dan pengoptimal kueri dapat predikat push down ke tabel dasar (baca: berkinerja baik dengan mereka). Hanya akan ada satu gabungan antara tabel supertype dan tabel subtype. Aplikasi Anda dapat diatur untuk membaca metadata untuk menemukan apa yang seharusnya dilakukan (atau dapat menggunakan tampilan yang dibuat secara otomatis dalam beberapa kasus).
Atau, jika Anda memiliki satu set subtipe multi-level, hanya beberapa yang bergabung. Maksud saya multi-level ketika beberapa subtipe berbagi kolom umum, tetapi tidak semua, Anda bisa memiliki tabel subtipe untuk mereka yang itu sendiri merupakan supertipe dari beberapa tabel lainnya. Misalnya, jika Anda menyimpan informasi tentang Server, Router, dan Printer, subtipe "Perangkat IP" antara bisa masuk akal.
Saya akan memberikan peringatan bahwa saya belum membuat basis data yang didekorasi dengan supertipe / subtipe hibrid EAV seperti yang saya sarankan di sini untuk dicoba di dunia nyata. Tetapi masalah yang saya alami dengan EAV tidak kecil, dan melakukan sesuatu mungkin mutlak harus jika database Anda akan besar dan Anda ingin kinerja yang baik tanpa beberapa perangkat keras raksasa yang mahal.
Menurut pendapat saya, waktu yang dihabiskan mengotomatiskan penggunaan / pembuatan / modifikasi tabel subtipe nyata pada akhirnya akan menjadi yang terbaik. Berfokus pada fleksibilitas yang didorong oleh data membuat EAV terdengar sangat menarik (dan percayalah, saya suka bagaimana ketika seseorang meminta saya untuk atribut baru pada tipe elemen saya dapat menambahkannya dalam waktu sekitar 18 detik dan mereka dapat segera mulai memasukkan data di situs web. ). Tetapi fleksibilitas dapat dicapai dalam lebih dari satu cara! Pra-pemrosesan adalah cara lain untuk melakukannya. Ini adalah metode yang sangat kuat yang hanya sedikit orang gunakan, memberikan manfaat menjadi data-driven sepenuhnya tetapi kinerja hard-coded.
(Catatan: Ya pandangan-pandangan itu benar-benar diformat seperti itu dan yang PIVOT benar-benar memiliki pemicu pembaruan. :) Jika seseorang benar-benar tertarik pada detail menyakitkan yang mengerikan dari pemicu UPDATE yang panjang dan rumit, beri tahu saya dan saya akan memposting sampel untuk Anda.)
Dan Satu Ide Lagi
Masukkan semua data Anda dalam satu tabel. Berikan kolom nama umum dan kemudian gunakan kembali / disalahgunakan untuk beberapa tujuan. Buat pandangan atas ini untuk memberi mereka nama yang masuk akal. Tambahkan kolom ketika kolom tipe data yang cocok tidak digunakan tidak tersedia, dan perbarui tampilan Anda. Meskipun panjang saya tentang subtipe / supertype, ini mungkin cara terbaik.