設計部品DB


8

(電気)部品を扱うツールを開発しています。パーツは、作成、表示、変更、削除、グループ化などができます...

この質問を将来の訪問者に役立つようにするために、DBのパーツを管理することは、DBのどのパーツ(CD、車、食べ物、学生など)に関係なく非常に一般的であるため、この質問を普遍的にします。

3つの異なるDB設計を考えています。

  1. 特殊なパーツ属性にパーツテーブルと派生テーブルを使用する。

    Parts      (id, part_type_id, name)
    PartTypes  (id, name)
    Wires      (id, part_id, lenght, diameter, material)
    Contacts   (id, part_id, description, picture)
    
  2. 専用のパーツテーブルのみを使用します。

    Wires      (id, name, lenght, diameter, material)
    Contacts   (id, name, description, picture)
    
  3. すべての値を含むParts-、PartTypes-、ValueTypes-、PartValuesテーブルを使用する。

    PartTypes  (id, name)
    ValueTypes (id, part_type_id, name)
    Parts      (id, part_type_id, name)
    PartValues (part_id, value_type_id, value)
    

どちらを選ぶのか、そしてその理由は?またはより良いものはありますか?
DBクエリが心配です。クエリが過度に遅くなったり複雑になったりしたくない。

更新

DB内の型の数は、国際標準に基づいており、ほとんど拡張されないため、ほとんど固定されています。


これは厳密にSQL DB(純粋にリレーショナル)に関するものですか、それともNOSQL DBもオプションですか?
c-smile

@ c-smile:私はまだNOSQLを扱っていませんので、それがオプションであるかどうかは本当にわかりません。私はすべてに開いています。
juergen d

回答:


16

オプション3:(時々)
オプション3は「EAV」設計です。理論的には、フィールドがテーブル構造から取り出されてデータになるので、それは素晴らしいことです。しかし、それはひどいパフォーマンスを与えます。また、適切なインデックス作成の使用を禁止します。また、クエリがはるかに複雑になります。

EAVは特別な状況でのみ使用します。EAVを使用して注文に必要な補助部品を計算しましたが、うまくいきました。ただし、コアテーブルのデザインとして使用することに非常にうんざりしてください。

オプション2:(なし?)
オプション2はノーノーです。共有フィールドはどうですか?すべての共有フィールドのテーブル構造を複製しますか?システム全体のレポートにユニオンを含める必要があります。

オプション1:(勝者!)
オプション1は少し基本的すぎるように見えるかもしれませんが、コアテーブルにはおそらく最善の策です。すべてのパーツが共有フィールドに同じマスターテーブルを使用するため、レポートでの結合が回避されます。インデックス作成を適切に使用できる優れたパフォーマンスを備えています。クエリは従来のスタイルで、シンプルです。

オプション1の欠点は、フィールドを動的に追加できないことです。しかし、本当にしたいですか?フィールドを動的に追加することにより、実行時にデータベース設計を実行します。


+1、しかし私の答えを見て、オプション#2の背後にある理論的根拠が何であるかを確認してください。
Doc Brown、

いくつかの考えの後、部品が規制ごとの絶対的な固定規格であるというOPのメモに基づいて、私はオプション#1と+1に同意します。他の誰もそれを言及していないため、将来的にも重要です:外部結合は一般的にパフォーマンス特性が低く、可能な場合は回避する必要があります。オプション#1は外部結合を含むため、これを追加するだけですが、この場合でも、おそらくコストに見合う価値があります。オプション#3には、独自のパフォーマンスの落とし穴があります。
Jimmy Hoffa 2013年

2
オプション1は基本的すぎるように見えるかもしれません。まさか、それは間違いなくそれを行う方法です。ジミーは間違っています。一般的に、外部結合のパフォーマンス特性は悪くありません。適切にインデックスを作成する限り、問題ありません。
Rocklan

6

私はオプション#3をしない傾向があります。

オプション#3は、正規化に違反する名前と値のペアのセットアップです。

理想的には、データベースのある程度の正規化を試みます。完全な正規化に努め、カスタマイズまたはパフォーマンスの問題が特定された場合は、必要に応じて非正規化します。

「銅製のすべてのワイヤーの名前とパーツIDは何ですか」というクエリを考えてみます。

構造#1は

select
  name, parts.id
from
  wire, parts
where
  wire.material = 'copper'
  and wire.part_id = parts.id

構造#2は

select id, name from wire where material = 'copper'

構造#3は

select
  parts.name,
  parts.id,
from
  parts, part_types, part_values, value_types
where
  part_types.name = "wire"
  and parts.part_type_id = part_types.id
  and value_types.name = "material"
  and value_types.id = part_values.type_value_id
  and part_values.value = "copper"

システムからの挿入と削除の複雑さも考慮してください。

なぜそうでないのかをさらに読む#3- 名前と値のペアの呪い


2
はい、名前と値のペアは悪です。おそらく#3はここでは不要ですが、これまでに見たテーブル構造が選択不能になり、名前と値のペアの形式に非正規化する必要が生じたように見えることがよくあります。しかし、それが修正された場合、おそらく#1が適切なアプローチです(クエリがさまざまな部分の集合に作用することを想定し、そうでない場合は#2が適切であると想定)
Jimmy Hoffa

また、あなたが使用していないが同様に参加するに行くだろう、where句の中に過度の仕事を入れ終わるれ、ここに参加するpart_type_id = part_types.idvalue_types.id = part_values.type_value_idの両方が部品の種類が線である場合、値型が材料であり、値が銅であるにどこ残し句に参加していますこれは比較的簡潔です
Jimmy Hoffa 2012

@JimmyHoffa私はそれが理想的なSQLではなくどのように見えるかを示すために簡単な短縮バージョンを実行していた。Redmineのテーブル構造で私が見た3番目のオプションでは、名前と値のペアがオンザフライでシステムに追加されます。新しいカスタムフィールドを追加するためにデータベースを更新する必要があるのは現実的ではないため、名前の値は適切な構造です。ただし、データベースクエリが少し遅くなり(インデックスは、型がすべての文字列になるほど満足ではありません)、クエリは少し見苦しくなります。

1
前回オプション#3を実行したのはMSSQLで、SQL_Variant型を使用しましたが、このようなインデックスは、文字列よりも少しだけ多いと思います。アプローチとあなたが言ったように、新しいタイプの一貫した成長があることがわかっているときが最善です。前回これを行ったのは、60列のテーブルを変換することでした。一貫して成長したキーごとに1つなので、これらのシナリオは明らかに発生しますが、おそらくこれはそれらの1つではなく、特定するのはOP次第です。
ジミーHoffa

4

オプション3に行く

オプション1は、結合をフィールド値に基づいて設定したくないため、不適切です。(すなわちIf type ="Wire" join to TblWire

オプション2は悪いです。全体として在庫についてレポートする方法がないからです。


また、オプション3は新しいパーツ属性のメンテナンス特性が最も優れていることに注意してください。このフォームはピボットフォームであるため、ピボットフォームとしてこのフォームを参照します(DBAにはその構造について共通の用語が欠落しているはずです)。 #1と#2で詳しく説明したより一般的な構造の場合、多くの場合、人々は#1を作成して新しいタイプの新しいテーブル/列を追加するだけなので、巨大な混乱を引き起こした後、頻繁に#3にピボットする必要があります彼らはもはや維持できません。
ジミーHoffa

オプション1の場合、結合前にタイプに「if」が必要になることはありません。正常に参加した場合は、タイプです。結合自体がフィルターを置き換える可能性があります。タイプを保存しないようにすることもできます。
mike30

@マイクは、2つの製品タイプが必要な場合はどうしますか?ケーブルが「ケーブル」に結合する場合、コネクタが「コネクタ」に結合する場合、彼が両方に結合する場合、何も得られません!参加を辞めた場合、彼は重複を取得します!
Morons 2012

@Morons。マスターとサブテーブルを左に結合します。calbles.IDがnullでなく、connectors.IDがnullでないフィルター。ビオラ!結合の成功をフィルターとして使用します。
mike30

2
@Morons:「悪夢」という言葉を繰り返しても、それは真実ではありません。新しいタイプが作成されるときに「すべてのコード」を変更する必要がある場合、「オプション1」または「オプション3」とは関係ありません。それは、コードがどれだけうまく構造化されているかをしなければなりません。そして、新しい要件が到着したときにいくつかの場所でコードを変更する必要があるのは「悪夢」ではなく、それは普通のことです(オプション3にも必要です)。さらに議論する前に、Entity-Attribute-Valueパターンが適切である場合とそうでない場合について知っておくことをお勧めします。EAVは時々アンチパターンです。
Doc Brown

4

継承を許可するデータ/オブジェクトモデルから始め、次に標準のオブジェクトリレーショナルマッピングを使用します。あなたは、基本クラスを取得この方法PartsなどのサブクラスWiresContactsあなたは「マップ-各クラス・ツー・オウン・テーブル」戦略を適用した場合などは、今、あなたが最も「正規化」ソリューションであるオプション1を取得し、期待するクエリについてこれ以上の情報がない場合は、標準的な戦略である必要があります。

オプション2は、「map-each-concrete-class-to-own-table」アプローチを適用したときに得られるものです。これにより、 "結合"を回避でき、クエリ(特に1つの "パーツタイプ"のみに対するクエリ)のパフォーマンスが向上する可能性があります。その一方で、すべてのパーツでの一般的な処理が困難で遅くなります。特別な理由がない場合は、これを避けてください。

オプション3は、実行時にユーザーがパーツタイプの数を変更できるようにする場合にのみ必要です。その要件が予期されない場合、オプション3は、過剰設計の完璧な例です。


2

NOSQL DBデータベース(MongoDBなど)では、「Parts」という名前のセットが1つだけ必要です。そのセットの各部分は、いわゆるドキュメントです。フィールドの変数セットを持つレコード:

{
   "_id": ObjectId("4efa8d2b7d284dea1"),
   "partType": "wire",
   "length": 102.5,
   "diameter": 1.5,
   "material": "silver"
}, 
{
   "_id": ObjectId("4efa8d2b7d284sjsq23d"),
   "partType": "contact",
   "description": "something",
   "picture": Binary(...)
}, 

これは、あなたが説明するタスクにとって最も自然なデータストレージだと思います。


2

間違いなくオプション1を使用しますが、いくつかの非常に単純な変更を加えます。

Parts      (id, part_type_id, name)
PartTypes  (id, name)
Wires      (id, part_id, part_type_id, lenght, diameter, material)
Contacts   (id, part_id, part_type_id, description, picture)

次に、CHECK制約とDEFAULT値を使用して、part_type_idが正しいことを確認し、part_type_idとpart_idの両方で結合できます。これにより、1つのテーブルのみに基づく条件付き結合が回避されます。また、part_type_idをワイヤーに追加する必要がある場合(そのパーツを細分割して、拡張属性の別のテーブルを追加する場合など)、デフォルトとチェックの制約を変更できます。


また、(安全に-一部のORMが単一列の主キーを必要としない限り)wires.idおよびcontacts.idを削除して(part_id, part_type_id)、パーツを一意に識別するのに十分な組み合わせにすることができます。
ypercubeᵀᴹ

@ypercubeは確かですが、この場合part_idは一意であるため、これを主キーとして使用し、必要に応じてpart_id、part_type_idに二次的な一意のインデックスを付けます。
Chris Travers

1

オプション3はより一般的で、より多くのユースケースに対応できます。

オプション3に進むと、単純な機能の結合と複雑なクエリがさらに必要になる場合があります。オプション2では、在庫やレポートなどの「大きな」機能の複雑なクエリが必要になり、そのためにはユニオンを使用する必要があります。

オプション3では、ビューを使用してクエリを常に簡略化できます。ワイヤまたはコンタクトのみが必要な場合は、それぞれにビューを作成します。あなたはそれを最適化することができた場合、それが必要になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.