web-dev-qa-db-ja.com

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

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

ERD1

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

Widget Sample ERD

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

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

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

https://stackoverflow.com/questions/9335548/how-to-structure-database-for-inventory-of-unlike-items

https://stackoverflow.com/questions/1249632/database-structure-for-items-with-varying-attributes

https://stackoverflow.com/questions/5559587/product-inventory-with-multiple-attributes

https://stackoverflow.com/questions/6613802/question-about-setting-up-inventory-database

https://stackoverflow.com/questions/514111/how-to-best-represent-items-with-variable-of-attributes-in-a-database

10
TheSecretSquad

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

スーパータイプ/サブタイプのパターンを調べてみませんか?共通の列は親テーブルに入ります。各特殊タイプには、親のIDを独自のPKとして持つ独自のテーブルがあり、すべてのサブタイプに共通ではない一意の列が含まれています。親テーブルと子テーブルの両方にタイプ列を含めて、各デバイスが複数のサブタイプにならないようにすることができます。 (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サウンドが非常に魅力的になります(そして、私を信じてlove誰かが要素タイプの新しい属性を要求したとき約18秒で追加でき、Webサイトにデータの入力をすぐに開始できます)。ただし、柔軟性は複数の方法で実現できます。前処理はそれを行う別の方法です。これは非常に強力な方法であるため、使用する人はほとんどいないため、完全にデータ駆動型であるという利点がありますが、ハードコード化されているというパフォーマンスをもたらします。

(注:はい、これらのビューは実際にそのようにフォーマットされており、PIVOTビューには実際に更新トリガーがあります。)誰かが本当に長くて複雑なUPDATEトリガーのひどい痛みを伴う詳細に興味がある場合は、私に知らせて投稿しますあなたのためのサンプル。)

そしてもう1つのアイデア

すべてのデータを1つのテーブルに入れます。列に一般的な名前を付け、複数の目的でそれらを再利用/乱用します。これらのビューを作成して、わかりやすい名前を付けます。適切なデータタイプの未使用の列が利用できない場合は列を追加し、ビューを更新します。サブタイプ/スーパータイプについて私の長さが続いているにもかかわらず、これは最良の方法かもしれません。

6
ErikE

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

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

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

ERD

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

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

6
Joel Brown
  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 ...(基本的にデバイスの他のすべての属性)

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