在庫アイテムにさまざまな属性がある場合の在庫データベース構造


10

エンタープライズハードウェア情報を格納するためのインベントリデータベースを構築しています。データベースがワークステーション、ラップトップ、スイッチ、ルーター、携帯電話などからの範囲を追跡するデバイス。デバイスのシリアル番号を主キーとして使用しています。私が抱えている問題は、これらのデバイスの他の属性がさまざまであり、他のデバイスに関係のないフィールドをインベントリテーブルに含めたくないということです。以下は、データベースの一部のERDへのリンクです(一部のFK関係は表示されていません)。たとえば、ワークステーションデバイスタイプのデバイスを電話テーブルに配置できないように設定しようとしています。これには、デバイスタイプまたはクラスを検証するために多くのトリガーを使用する必要があり、異なる属性を持つ異なるデバイスが追跡されるときはいつでも新しいテーブルが使用されます。

ERD1

シリアル番号にマップできる属性テーブルの設定を調べましたが、デバイスタイプに適用されない属性をデバイスに割り当てることができます。たとえば、必要に応じて誰かがワークステーションに電話番号属性を割り当てることができます。 。このサイトで次のような構造の説明を見つけました。

ウィジェットサンプルERD

この構造は、属性がすべて私が保存しているアイテムに適用できる場合に最適です。たとえば、データベースに携帯電話のみが格納されている場合、属性は、タッチスクリーン、トラックパッド、キーボード、4G、3Gなどです。その場合、それらはすべて電話に適用されます。データベースには、hostname、circuitType、phoneNumberなどの属性があり、特定のタイプのデバイスにのみ適用されます。

特定のデバイスタイプに適用される属性のみがそのタイプのデバイスに割り当てられるように設定します。このデータベースのセットアップ方法に関する提案はありますか?これが1対1の関係の適切な使用であるかどうか、またはこれを行うより良い方法があるかどうかはわかりません。お時間を割いていただき、誠にありがとうございます。

ここに私が読んだ他のスレッドのいくつかがあります。彼らは私にいくつかの良い洞察を与えました、しかし私は彼らが本当に適用するとは思いません:

/programming/9335548/how-to-structure-database-for-inventory-of-unlike-items

/programming/1249632/database-structure-for-items-with-varying-attributes

/programming/5559587/product-inventory-with-multiple-attributes

/programming/6613802/question-about-setting-up-inventory-database

/programming/514111/how-to-best-represent-items-with-variable-of-attributes-in-a-database

回答:


6

スーパータイプ/サブタイプ

スーパータイプ/サブタイプのパターンを調べてみませんか?共通の列は親テーブルに入ります。それぞれの特殊タイプには、独自の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つのテーブルに入れます。列に一般的な名前を付けて、複数の目的で再利用/乱用します。これらのビューを作成して、わかりやすい名前を付けます。適切なデータ型の未使用の列が利用できない場合は列を追加し、ビューを更新します。サブタイプ/スーパータイプについて私の長さが続いているにもかかわらず、これが最善の方法かもしれません。


各サブタイプテーブルに親からのPKと一般的でないフィールドが含まれているこの設計について考えました。親フィールドと各サブタイプテーブルにタイプフィールドを配置し、それらにCHECK制約を設定できると思いました。新しいタイプのデバイスを追跡する必要があるときはいつでも新しいテーブルが必要であり、多くの1対1の関係があるため、この設計は避けることにしました。乱雑で柔軟性に欠けるように見えました。私はあなたの入力に感謝します。
TheSecretSquad

私は、ビジネスで使用されているEAVデータベースを構築しました。感謝します。データのセットは小さいので(アイテムの種類は数十あります)、パフォーマンスは悪くありません。しかし、データベースに数千以上のアイテムが含まれている場合はそうなります。この経験から、EAVデータベースはクエリが非常に難しいため、可能な限り将来はEAVデータベースを避けたいと強く思っています。
ErikE 2012年

また、実際のサブタイプテーブルの使用/作成/変更の自動化に費やされた時間は、私の意見では、最終的には最良だと思います。
ErikE 2012年

EAVパターンを調べた後、属性の値はデータ型(この場合はすべて文字列)を共有する必要があることに気付きました。また、EAV設定のクエリは面倒です。スーパータイプ/サブタイプは良く見えています。私の質問ですが、特定のテーブルでは特定のデバイスタイプしか許可されていません。すべてのテーブルにデバイスクラスID(電話、コンピューター、ルーター)を挿入してこれを検証し、そのフィールドにチェック制約を設定しますか、それともサブタイプテーブルからそのフィールドを除外して、それぞれにトリガーを使用しますか?参照としてERD3を参照してください。
TheSecretSquad

1
EAVデータをクエリする場合、クエリするデータのリレーショナルテーブルのデータマートを作成し、いくつかのスクリプトを使用してデータを入力することは珍しくありません。クエリはより高速に実行されますが、データマートに入力したデータに対してのみであり、セットアップにはかなりの計画が必要です。
FrustratedWithFormsDesigner

6

あなたの場合、最善のアプローチは、Entity-Attribute-Value(EAV)モデルのバリエーションです。EAVはある意味で役に立たず、多くの場合は誤用されるため、EAVを避けている人はたくさんいます。ただし、EAVは特定の要件に対して適切に機能するソリューションです。

状況に含めたいバリエーションは、エンティティ(つまり、在庫アイテム)から1レベル離れた属性を抽象化することです。基本的に、属性のリストを持つデバイスタイプを定義する必要があります。次に、そのタイプのデバイスが持つはずの各属性の値を持つデバイスインスタンスを定義します。

ここにERDのスケッチがあります:

ERD

DEVICE_ATTRIBUTEジェネリック属性の各タイプの値が含まれています。 DEVICE_TYPE特定のタイプのデバイスに適用される一般的な属性のリストを定義します(これらはTYPICAL_DEVICE_ATTRIBUTEs

これにより、デバイスに入力する必要がある属性を制御しながら、異なるタイプのデバイスに異なる属性のリストを持たせることができます。また、属性を互いに並べることで、デバイス間での比較が容易になります。


これは、ssmusokeが推奨するものに似ています。私は彼の推奨事項を使用して自分のERDを変更しましたが、あなたのERDと一致しているようです。http://www.dividegraphics.com/ERD2.jpgで新しいRDをチェックして、フィードバックを提供してください
TheSecretSquad 2012年

@reallythecrash-あなたは正しいです。私はssmusokeと同じ基本的なアプローチを提案しています。モデルの構造と、EAVを使用する根拠の両方を理解しやすくすることを期待して、私の答えに別のタックを入れました多くの人々が(不当に)アンチパターンであると非難します。
Joel Brown

いくつかの調査の結果、人々がEAVをアンチパターンと見なす理由がわかります。EAVを使用してデータを格納するのは簡単ですが、データ型のクエリと保守は特に複雑だと思います。これは目的が狭いパターンだと思います。私ではなく、適切に実装できる経験豊富な開発者が使用する必要があります。私はおそらくスーパータイプ/サブタイプのパラダイムを選ぶでしょう。
TheSecretSquad

@JoelBrown-その図をスケッチするためにどのソフトウェアを使用しましたか?
Vidar

@Vidar-James Martinの視覚的な規則を使用するために作成したERD smartshapesでVisioを使用し、スケッチのカスタム線パターンで描画しました。これはクイック/ドラフトデータモデルに使用するのに適したツールです。ダイアグラムが形式的すぎると、一部の人々はそれが完成したと考えるようになる可能性があります。そのため、大まかなデータモデルは、データモデルの完成度や完成度についての結論にジャンプするのを防ぐのに役立ちます。
Joel Brown

1
  1. 全体的なアプローチは次のとおりです。

a)異なるデバイスの属性をデバイスタイプに取り組むためのEntity-Attribute-Valueモデルアプローチ。各デバイスタイプには、追跡する値を持つ属性のリストがあります。

b)デバイスタイプごとに、1つのデバイスに対応するシリアル番号でインベントリの詳細を追跡します。

  1. したがって、最終的には次の表になります。

a)属性-すべてのデバイスの属性を定義します(すべてがこの表に含まれます)列:ID、名前、説明

b)項目属性-特定のデバイスに許可される属性を定義します-itemid、attributeid

c)アイテム定義-Black Berry Torch 4500、Iphone 4S、Iphone 3Sなどのアイテムを定義します-id、名前、説明、categoryid(携帯電話、スイッチなどのカテゴリを追加する場合)

d)デバイス-個々のデバイス-id、itemid、inventorydate、deactivatedate、serialnumber ...(基本的にデバイスの他のすべての属性)

デバイスのトランザクションに関するその他の情報を追跡する場合は、必要に応じて、デバイスにリンクされたテーブルをさらに追加できます。


ご意見ありがとうございます。これは私が探しているものと一致しています。どうすればいいのかわからなかったのです。私はあなたの仕様を反映するようにERDを変更しました。各デバイスタイプに許可されるすべての属性を入力するには、より多くの作業が必要と思われますが、最大限の柔軟性を提供するようにも見えます。小さなプロトタイプを作成して、思ったように機能するかどうかを確認します。再度、感謝します。確認したい場合は変更を加えたERDをアップロードし、順調に進んでいるかどうかをお知らせください。http://www.dividegraphics.com/ERD2.jpg
TheSecretSquad 2012年

はい、あなたは正しい軌道に乗っています。
Stephen Senkomago Musoke 2012年

EAVは多くの柔軟性を提供しますが、機能を維持するために、より多くのメタデータを利用できます。
FrustratedWithFormsDesigner 2012年

@FrustratedWithFormsDesignerは、システムがさまざまなアイテム、電話、スイッチ、PC、ラップトップなどを格納する場合、避けられないように見えます...私が言うよりも多くのテーブルよりも優れたメタデータ
Stephen Senkomago Musoke 2012年

1
@ssmusoke:同意しますが、メタデータの重要性を人々が認識できず、EAVの実装が悪夢になるため、その点を強調したいと思います。
FrustratedWithFormsDesigner 2012年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.