質問

私の雇用主である小規模な事務用品会社はサプライヤーを変更しており、私はその電子コンテンツを調べて堅牢なデータベース スキーマを考え出しています。以前のスキーマはほとんど何も考えずにただ組み合わされたもので、破損した一貫性のない情報を含む耐え難いデータ モデルが完成しました。

新しいサプライヤーのデータは古いサプライヤーのデータよりもはるかに優れていますが、彼らのデータは私が言うところのデータです。 超正規化された. 。たとえば、同社の製品カテゴリ構造には 5 つのレベルがあります。マスター部門、部門、クラス、サブクラス、製品ブロック。さらに、製品ブロックのコンテンツには、製品の長い説明、検索語、画像名が含まれています (製品ブロックには製品とすべてのバリエーションが含まれるという考えです。特定のペンには、黒、青、または赤のインクが付いている場合があります。これらの項目はすべて本質的に同じものであるため、単一の製品ブロックに適用されます)。私が与えられたデータでは、これは製品ブロックの一意の ID への参照を持つ製品テーブル (「テーブル」と言っていますが、データを含むフラット ファイルです) として表現されています。

提供されたデータを比較的すぐにロードする必要があるため、提供されたデータに対応する堅牢なスキーマを考え出そうとしていますが、提供されたデータはデータの種類と一致していないようです。サンプル Web サイトでデモンストレーションを提供します (http://www.iteminfo.com)。いずれにしても、私は彼らのプレゼンテーション構造を再利用するつもりはないので、議論の余地はありませんが、物事をどのように構造化するかについてアイデアを得るためにサイトを閲覧していました。

私が迷っているのは、データをこの形式で保持すべきかどうか、あるいは、たとえば、自己参照関係を使用してマスター/部門/クラス/サブクラスを単一の「カテゴリ」テーブルに統合し、それを製品ブロック (製品ブロックは「カテゴリ」そのものではなく、特定のカテゴリに関連する製品のグループであるため、個別に保持する必要があります)。現在、プロダクト ブロック テーブルはサブクラス テーブルを参照しているため、これらを統合すると、これは「category_id」に変更されます。

私はおそらく、Ruby on Rails でこのデータを利用して電子商取引のストアフロントを作成するつもりです (または、いずれにせよ、それが私の計画です)。そのため、後で問題が発生したり、アプリケーションが肥大化したりすることを避けようとしています。考えすぎていますが、後悔するよりは安全を選びたいです。以前のデータは非常に混乱しており、一貫性と不正確なデータにより会社に数万ドルの売上損失が発生しました。また、データベースが堅牢で制約が適用されるようにすることで、Rails の慣例から少し脱却する予定です (アプリケーション レベルでも行う予定です)。その点も考慮する必要があります。

このような状況にどう対処しますか?ロードするデータは、テーブル構造を模倣したフラット ファイルにすでに存在していることに注意してください (どの列がどのようなもので、どのような参照が設定されているかを示すドキュメントがあります)。現在のように正規化したままにしておくべきか、それとも統合を検討すべきか判断しようとしています。Rails を使用してサイトをプログラミングする方法に各メソッドがどのような影響を与えるかを認識する必要があります。統合すると、基本的に 4 つの「レベル」のカテゴリが 1 つのテーブルに存在することになりますが、その方が、個別のテーブルよりも管理しやすいように思えます。各レベルは、サブクラス (製品ブロックに直接リンクする) を除いて、 する その下のカテゴリの次のレベルを表示する以外のすべて。このようなデータを処理する「最善の」方法については、いつも迷ってしまいます。「苦痛になるまで正規化し、うまくいくまで非正規化する」という格言は知っていますが、これまで実際にそれを実装する必要があったことはありません。

役に立ちましたか?

解決

私は、非正規化データ モデルよりも「超正規化」アプローチを好みます。あなたが言及した自己参照テーブルは、テーブルの数を減らし、ある意味で作業を簡素化する可能性がありますが、一般に、このタイプの関係は対処が難しい場合があります。階層クエリは、オブジェクト モデルをこれにマッピングするのと同様に、面倒になります (その方法を選択した場合)。

結合をいくつか追加しても問題はなく、アプリケーションの保守性が向上します。過剰な結合によってパフォーマンスが低下しない限り、現状のままにしておくつもりです。さらに、これらのレベルのテーブルのいずれかに追加機能の追加が必要な場合でも、それらをすべて自己参照テーブルにマージしているため、問題が発生することはありません。

他のヒント

私は、親子階層の自己参照テーブル構造に関する批判には全く同意しません。リンク リスト構造により、ほとんどの場合、UI およびビジネス レイヤーのプログラミングが容易になり、保守しやすくなります。これは、リンク リストとツリーが、UI レイヤーとビジネス レイヤーが通常実装される言語でこのデータを表現する自然な方法であるためです。

これらの構造でデータ整合性制約を維持するのが難しいという批判は完全に正当ですが、簡単な解決策は、より厳しいチェック制約をホストするクロージャ テーブルを使用することです。クロージャーテーブルはトリガーで簡単に維持できます。

トレードオフとして、UI とビジネス層のコードの複雑さが大幅に軽減される代わりに、DB (クロージャ テーブルとトリガー) が少し複雑になります。

私の理解が正しければ、個別のテーブルを取得して、自己参照 FK を持つ単一のテーブルに保持される階層に変換したいことになります。

これは一般に、より柔軟なアプローチ (たとえば、5 番目のレベルを追加する場合) ですが、SQL およびリレーショナル データ モデルは、MS SQL Server CTE のような新しい構文であっても、このようなリンク リストではうまく機能しない傾向があります。確かに、CTE を使用すると、さらに改善されます。

製品は常に階層の 4 番目のレベルになければならないなど、強制するのは困難でコストがかかる場合があります。

この方法でやると決めた場合は、必ず Joe Celko の記事をチェックしてください。 賢い人のための SQL, SQL でのモデリングと階層の操作に関するセクションが 1 ~ 2 つあると思います。あるいは、このテーマに特化した彼の本を入手したほうがよいでしょう (Joe Celko の Smarties のための SQL のツリーと階層).

Normalization データの整合性、つまり次のことを意味します。各正規形により、データが矛盾する状況の数が減ります。

原則として、 denormalization より速くするという目標がある querying, 、しかしスペースの増加につながります、 DML そして最後に重要なこととして、データの一貫性を保つための取り組みが強化されました。

通常、コードの記述が速くなり (コードの記述が速いのではなく、記述が速くなります)、データが次の場合にはコードでエラーが発生しにくくなります。 normalized.

自己参照テーブルは、ほとんどの場合、正規化されたテーブルよりもクエリがはるかに悪く、パフォーマンスも悪くなることが判明します。やめてください。これはより洗練されているように見えるかもしれませんが、実際はそうではなく、非常に貧弱なデータベース設計手法です。個人的には、あなたが説明した構造は、超正規化されていないように思えます。適切に正規化されたデータベース (外部キー制約、デフォルト値、トリガー (複雑なルールに必要な場合)、およびデータ検証制約を備えたもの) には、一貫性のある正確なデータが含まれる可能性も高くなります。データベースにルールを適用させることについては同意します。おそらく、これが、ルールが適切な場所で適用されず、人々が簡単にルールを回避できたため、最後のアプリケーションで不正なデータが発生した理由の一部であると考えられます。アプリケーションが同様にチェックすべきではないというわけではありません(たとえば、データベースの挿入が失敗するために無効な日付を送信しても意味がありません)。再設計するので、完全に普通の正規化された構造をよりエレガントに見せることよりも、必要な制約を設計し、正しいデータ型を選択すること (日付を文字列データとして保存しないなど) に多くの時間と労力を費やします。

私はそれをできるだけ彼らのモデルに近づけます(そして可能であれば、フラット化されたバージョンではなく、彼らのスキーマに一致するファイルを取得します)。データをモデルに直接取り込んだ場合、送信されたデータが内部アプリケーションのモデルへの変換の前提を破り始めたらどうなりますか?

データを取り込んで健全性チェックを実行し、前提条件に違反していないか確認する方がよいでしょう。次に、アプリケーション固有のモデルがある場合は、アプリケーションで最適に使用できるモデルに変換します。

非正規化しないでください。非正規化によって適切なスキーマ設計を達成しようとすることは、ニューヨークから車で離れてサンフランシスコに行こうとするようなものです。どちらに進むべきかはわかりません。

あなたの状況では、正規化されたスキーマが何を望むかを理解したいと考えています。これは主にソース スキーマに基づいて行うことができますが、データ内の関数依存関係 (FD) が何であるかを学習する必要があります。ソース スキーマもフラット化されたファイルも、すべての FD を明らかにすることは保証されていません。

正規化されたスキーマがどのようなものになるかを理解したら、次はニーズを満たすスキーマを設計する方法を理解する必要があります。スキーマが完全に正規化されていないとしても、それは問題ありません。ただし、フラット化されたファイル内のデータと設計したスキーマ内のデータ間の変換をプログラミングする際には、困難を覚悟してください。

あなたの会社の以前のスキーマは、一貫性と不正確さのために数百万ドルのコストがかかっていたとおっしゃいました。スキーマが正規化されるほど、内部の不整合から保護されます。これにより、不正確さについてより警戒することができます。一貫して間違っているデータは、一貫性のないデータと同じくらい誤解を招く可能性があります。

あなたのストアフロント (またはあなたが構築しているものは何であれ、それについてはよくわかりませんが) は常にこのサプライヤーからのデータを使用する予定ですか?サプライヤーを変更したり、別のサプライヤーを追加したりする可能性はありますか?

その場合は、次の条件を満たす一般的なスキーマを設計します。 あなたの ニーズを特定し、それにベンダー データをマッピングします。個人的には、カテゴリ バリアントの 4 つの (一見ほとんど役に立たない) レベルを維持し、翌年に 5 番目のレベルが追加されたことに気づくよりも、自己参照カテゴリ (階層) テーブルの (信じられないほど小さな) '痛み' に耐えたいと思っています。または 3 つだけの製品ラインを導入しました...

私にとって本当の疑問は次のとおりです。 モデルに最も適合するものは何ですか?

タプルとリストを比較するようなものです。

  1. タプルはサイズが固定されており、異種混合であり、「超正規化」されています。
  2. リストのサイズは任意であり、同種です。

タプルが必要な場合はタプルを使用し、リストが必要な場合はリストを使用します。それらは基本的に異なる目的に役立ちます。

この場合、 製品構造はすでに明確に定義されています (変更する可能性は低いと思います) その場合は、「タプル アプローチ」を使い続けると思います。 リスト (または再帰テーブル パターン) の真の威力/使用法は、次のことが必要な場合です。 拡大する BOM や家系図などの任意の深さまで。

私は必要に応じて、データベースの一部で両方のアプローチを使用します。 ただし、再帰的パターンには「隠れたコスト」もあります。それは、すべての ORM (AR についてはわかりません) がそれを十分にサポートしているわけではないということです。最新の DB の多くは、「結合スルー」 (Oracle)、階層 ID (SQL Server)、またはその他の再帰的パターンをサポートしています。もう 1 つのアプローチは、セットベースの階層 (通常はトリガー/メンテナンスに依存します) を使用することです。いずれの場合でも、使用されている ORM が再帰クエリを十分にサポートしていない場合は、手動でのクエリ/ビューの生成やトリガーなどの管理の観点から、DB 機能を直接使用することで余分な「コスト」が発生する可能性があります。ファンキーな ORM を使用していない場合、または単に iBatis などの論理区切りを使用している場合、この問題は当てはまらない可能性があります。

パフォーマンスに関しては、新しい Oracle または SQL Server (およびおそらく他の) RDBMS 上でもほぼ同等であるはずなので、その点は私の心配はほとんどありません。ただし、RDBMS と移植性の問題に対して利用可能なソリューションを確認してください。

自己参照テーブルを使用するというオプションだけを考慮して、データベースに階層を導入しないことを推奨している皆さん。これは、データベース内の階層をモデル化する唯一の方法ではありません。再帰クエリを使用せずに、より簡単かつ高速なクエリを実現する別のアプローチを使用することもできます。階層内に大きなノード (カテゴリ) のセットがあるとします。

Set1 = (ノード1 ノード2 ノード3...)

このセット内の任意のノードは、それ自体が他のノードまたはネストされたセットを含む別のセットになることもできます。

ノード1=(ノード2 ノード3=(ノード4 ノード5=(ノード6) ノード7))

さて、それをどのようにモデル化できるでしょうか?各ノードに、ノードに含まれるノードの境界を設定する 2 つの属性を持たせましょう。

ノード = { ID:int、最小:int、最大:int }

階層をモデル化するには、これらの最小/最大値をそれに応じて割り当てるだけです。

ノード 1 = { ID = 1、最小 = 1、最大 = 10 }
ノード 2 = { ID = 2、最小 = 2、最大 = 2 }
ノード 3 = { ID = 3、最小 = 3、最大 = 9 }
ノード 4 = { ID = 4、最小 = 4、最大 = 4 }
Node5 = { ID = 5、最小 = 5、最大 = 7 }
ノード6 = { ID = 6、最小 = 6、最大 = 6 }
Node7 = { ID = 7、最小 = 8、最大 = 8 }

ここで、Set/Node5 の下のすべてのノードをクエリするには、次のようにします。

n.* をノードとして n、ノードとして s から選択
ここで、s.Id = 5、s.Min < n.Min、n.Max < s.Max

唯一リソースを消費する操作は、新しいノードを挿入する場合、または階層内で一部のノードを移動する場合です。これは、多くのレコードが影響を受けるためですが、階層自体は頻繁に変更されないため、これは問題ありません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top