スーパータイプ/サブタイプ
スーパータイプ/サブタイプのパターンを調べてみませんか?共通の列は親テーブルに入ります。それぞれの特殊タイプには、独自のPKとして親のIDを持つ独自のテーブルがあり、すべてのサブタイプに共通ではない固有の列が含まれています。親テーブルと子テーブルの両方にタイプ列を含めて、各デバイスが複数のサブタイプにならないようにすることができます。(ItemID、ItemTypeID)の子と親の間にFKを作成します。スーパータイプテーブルまたはサブタイプテーブルのいずれかにFKを使用して、他の場所で必要な整合性を維持できます。たとえば、任意のタイプのItemIDが許可されている場合は、親テーブルへのFKを作成します。SubItemType1のみを参照できる場合は、そのテーブルへのFKを作成します。TypeIDは参照テーブルから除外します。
ネーミング
名前付けに関しては、私が見るように2つの選択肢があります( "ID"のみの3番目の選択肢は私の考えでは強力なアンチパターンであるため)。親テーブルにあるようにサブタイプキーItemIDを呼び出すか、またはDoohickeyIDなどのサブタイプ名を呼び出します。これについてのいくつかの考えといくつかの経験の後、私はそれをDoohickeyIDと呼ぶことを提唱します。この理由は、サブタイプテーブルが(Doohickeysではなく)アイテムを含む偽装で実際に混乱している可能性があるにもかかわらず、DoohickeyテーブルへのFKを作成し、列名がそうでない場合と比べると、これは小さなネガティブです。一致!
EAVを使用するかどうか-EAVデータベースを使用した経験
EAVが本当にやらなければならないことなら、それはあなたがやらなければならないことです。しかし、それがあなたがしなければならなかった場合はどうなりますか?
私は、ビジネスで使用されているEAVデータベースを構築しました。感謝します。データのセットは小さいので(アイテムの種類は数十あります)、パフォーマンスは悪くありません。しかし、データベースに数千を超えるアイテムが含まれていると、それは悪いことです。さらに、テーブルはクエリが非常に困難です。この経験から、私は将来、可能な限りEAVデータベースを避けたいと強く思っています。
次に、データベースに、存在するすべてのサブタイプのPIVOTされたビューを自動的に構築するストアドプロシージャを作成しました。AutoDoohickeyからクエリを実行できます。サブタイプに関する私のメタデータには、ビュー名での使用に適したオブジェクトセーフ名を含む「ShortName」列があります。ビューも更新可能にしました!残念ながら、それらを結合で更新することはできませんが、既存の行を挿入して、UPDATEに変換することはできます。残念ながら、INSERTからUPDATEへの変換プロセスで更新する列をVIEWに指示する方法がないため、いくつかの列だけを更新することはできません。 「この列を更新しないでください。」
EAVデータベースを使いやすくするためのこの装飾にもかかわらず、SLOWであるため、これらのビューはほとんどの通常のクエリで使用しません。クエリ条件は、Value
テーブルに完全にプッシュされる述語ではないため、フィルタリングする前に、そのビューのタイプのすべてのアイテムの中間結果セットを構築する必要があります。痛い。だから私は多くの、多くの多くの結合を持つ多くのクエリを持っています、それぞれが異なる値を取得するために出かけます。それらは比較的よく機能しますが、痛いです!ここに例があります。これを作成するSP(およびその更新トリガー)は1つの巨大な獣であり、私はそれを誇りに思っていますが、これを維持しようとするものではありません。
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->クラスター->サーバー):
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データベースの動的な側面をいくつか持つ必要がある場合は、そのようなデータベースがあるかのようにメタデータを作成することを検討できますが、実際にはスーパータイプ/サブタイプの設計パターンを使用します。はい、新しいテーブルを作成し、列を追加、削除、変更する必要があります。しかし、適切な前処理を行うと(EAVデータベースの自動ビューで行ったように)、実際のテーブルのようなオブジェクトを操作できます。ただ、それらは私のものほど危険ではなく、クエリオプティマイザーはベーステーブルへのプッシュダウンを述語することができます(読み取り:それらでうまく実行します)。スーパータイプテーブルとサブタイプテーブルの間の結合は1つだけです。アプリケーションは、メタデータを読み取って何をすべきかを発見するように設定できます(場合によっては自動生成ビューを使用できます)。
または、サブタイプのマルチレベルセットがある場合は、ほんの数個の結合です。マルチレベルとは、すべてではなく一部のサブタイプが共通の列を共有する場合、それ自体が他のいくつかのテーブルのスーパータイプであるサブタイプテーブルを持つことができるという意味です。たとえば、サーバー、ルーター、およびプリンターに関する情報を格納している場合、「IPデバイス」の中間サブタイプが理にかなっています。
ここで提案しているように、スーパータイプ/サブタイプのハイブリッドEAVメタデータ装飾データベースをまだ作成していないため、実際に試してみる必要はありません。しかし、EAVで経験した問題はささいなものではなく、データベースが大きくなり、クレイジーで高価な巨大なハードウェアなしで優れたパフォーマンスを望む場合、何かをすることはおそらく絶対に必要です。
私の意見では、実際のサブタイプテーブルの使用/作成/変更の自動化に費やされた時間は、最終的には最良だと思います。データに基づく柔軟性に重点を置くと、EAVサウンドが非常に魅力的になります(誰かが要素タイプの新しい属性を要求されたときに約18秒で追加でき、Webサイトにデータの入力をすぐに開始できるのが気に入っています)。ただし、柔軟性は複数の方法で実現できます。前処理はそれを行う別の方法です。これは非常に強力な方法であるため、使用する人はほとんどいないため、完全にデータ駆動型であるという利点がありますが、ハードコード化されているというパフォーマンスをもたらします。
(注:はい、これらのビューは実際にそのようにフォーマットされており、PIVOTビューには実際に更新トリガーがあります。あなたのためのサンプル。)
そしてもう一つのアイデア
すべてのデータを1つのテーブルに入れます。列に一般的な名前を付けて、複数の目的で再利用/乱用します。これらのビューを作成して、わかりやすい名前を付けます。適切なデータ型の未使用の列が利用できない場合は列を追加し、ビューを更新します。サブタイプ/スーパータイプについて私の長さが続いているにもかかわらず、これが最善の方法かもしれません。