24

さまざまな車両の衝突試験データを格納するデータベースをセットアップしているとします。スピードボート、乗用車、ゴーカートの衝突試験のデータを保存したいと考えています。

SpeedboatTests、CarTests、および GokartTests の 3 つの個別のテーブルを作成できます。ただし、多くの列は各テーブルで同じになります (たとえば、テストを実行した人の従業員 ID、衝突の方向 (正面、側面、背面) など)。ただし、多数の列が異なるため、すべてのテスト データを 1 つのテーブルにまとめることは望ましくありません。スピードボートでは常に null になる列がかなり多く、常に null になる列がかなりあるからです。車の場合は null になり、ゴーカートの場合は常に null になるものがかなりあります。

テストに直接関係のない情報 (テスト対象の設計者の従業員 ID など) も保存したいとします。これらの列は、特に同じ車両のすべてのテストで繰り返されるため、「テスト」テーブルに配置するのは適切ではないようです。

関連する質問を確認できるように、考えられるテーブルの配置を 1 つ示しましょう。

スピードボート
ID | col_about_speedboats_but_not_tests1 | col_about_speedboats_but_not_tests2

車
ID | col_about_cars_but_not_tests1 | col_about_cars_but_not_tests2

ゴーカート
ID | col_about_gokarts_but_not_tests1 | col_about_gokarts_but_not_tests2

テスト
ID | タイプ | id_in_type | col_about_all_tests1 | col_about_all_tests2
(id_in_type は、次の 3 つのテーブルのいずれかの id 列を参照します。
タイプの値に応じて)

スピードボートテスト
ID | スピードボート_id | col_about_speedboat_tests1 | col_about_speedboat_tests2

車のテスト
ID | car_id | col_about_car_tests1 | col_about_car_tests2

GokartTests
ID | gokart_id | col_about_gokart_tests1 | col_about_gokart_tests2

この構造の良い点/悪い点、およびこのようなものを実装するための好ましい方法は何ですか?

Vehicles テーブルに含めたいすべての車両に適用される情報もある場合はどうでしょうか。CarTests テーブルは次のようになりますか...

ID | vehicle_id | ...

次のような Vehicles テーブルを使用します。
ID | タイプ | id_in_type
(スピードボート、車、またはゴーカートのいずれかの ID を指す id_in_type を使用)

これは王室の混乱になりつつあるようです。このようなものをどのように設定する必要がありますか?

4

6 に答える 6

41

および設計は、ポリモーフィック アソシエーションtypeと呼ばれます。この設計は、複数の点で正規化のルールを破っています。少なくとも、いくつかのテーブルのいずれかを参照する可能性があるため、実際の外部キー制約を宣言できないことを警告する必要があります。id_in_typeid_in_type

テーブルを定義するより良い方法は次のとおりです。

  • Vehiclesすべての車両サブタイプと車両テストの抽象参照ポイントを提供する抽象テーブルを作成します。
  • 各車両サブタイプには、自動インクリメントではなく を参照する主キーがありますVehicles
  • 各テスト サブタイプには、自動インクリメントしない主キーがありますが、代わりに を参照しTestsます。
  • 各テスト サブタイプには、対応する車両サブタイプへの外部キーもあります。

サンプル DDL は次のとおりです。

CREATE TABLE Vehicles (
 vehicle_id INT AUTO_INCREMENT PRIMARY KEY
);

CREATE TABLE Speedboats (
 vehicle_id INT PRIMARY KEY,
 col_about_speedboats_but_not_tests1 INT,
 col_about_speedboats_but_not_tests2 INT,
 FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id)
);

CREATE TABLE Cars (
 vehicle_id INT PRIMARY KEY,
 col_about_cars_but_not_tests1 INT,
 col_about_cars_but_not_tests2 INT,
 FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id)
);

CREATE TABLE Gokarts (
 vehicle_id INT PRIMARY KEY,
 col_about_gokarts_but_not_tests1 INT,
 col_about_gokarts_but_not_tests2 INT,
 FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id)
);

CREATE TABLE Tests (
 test_id INT AUTO_INCREMENT PRIMARY KEY,
 col_about_all_tests1 INT,
 col_about_all_tests2 INT
);

CREATE TABLE SpeedboatTests (
 test_id INT PRIMARY KEY,
 vehicle_id INT NOT NULL,
 col_about_speedboat_tests1 INT,
 col_about_speedboat_tests2 INT,
 FOREIGN KEY(test_id) REFERENCES Tests(test_id),
 FOREIGN KEY(vehicle_id) REFERENCES Speedboats(vehicle_id)
);

CREATE TABLE CarTests (
 test_id INT PRIMARY KEY,
 vehicle_id INT NOT NULL,
 col_about_car_tests1 INT,
 col_about_car_tests2 INT,
 FOREIGN KEY(test_id) REFERENCES Tests(test_id),
 FOREIGN KEY(vehicle_id) REFERENCES Cars(vehicle_id)
);

CREATE TABLE GokartTests (
 test_id INT PRIMARY KEY,
 vehicle_id INT NOT NULL,
 col_about_gokart_tests1 INT,
 col_about_gokart_tests2 INT,
 FOREIGN KEY(test_id) REFERENCES Tests(test_id),
 FOREIGN KEY(vehicle_id) REFERENCES Gokarts(vehicle_id)
);

Tests.vehicle_id別の方法として、各テスト サブタイプ テーブルで参照先を宣言しVehicles.vehicle_idて vehicle_id 外部キーを削除することもできますが、それではゴーカートの ID を参照するスピードボート テストなどの異常が許容されます。

于 2009-02-16T21:42:53.013 に答える
14

継承階層をデータベース テーブルにマッピングする方法については、Martin Fowler が彼の著書 Patterns of Enterprise Application Architecture でかなりうまく代替案を示していると思います。

http://martinfowler.com/eaaCatalog/singleTableInheritance.html

http://martinfowler.com/eaaCatalog/classTableInheritance.html

http://martinfowler.com/eaaCatalog/concreteTableInheritance.html

サブクラスの追加フィールド/列の数が少ない場合は、通常、単一テーブルの継承が最も簡単に処理できます。

データベースに PostgreSQL を使用していて、データベース固有の機能に結び付けたい場合は、テーブルの継承が直接サポートされます。

http://www.postgresql.org/docs/8.3/static/ddl-inherit.html

于 2009-02-16T21:05:00.733 に答える
0

Vehicle (ID、タイプなど) VehicleAttributes ()VehicleID、AttributeID、Value)、CrashTestInfo (VehicleID、CrashtestID、Date など) CrashtestAttributes(CrashTestID、AttributeID、Value) など、さまざまなテーブルに分割します。

または、属性ではなく、記録する必要がある同様の詳細のセットごとにテーブルを分けます。

于 2009-02-16T21:00:39.513 に答える
0

Python のオブジェクト リレーショナル マッパーであるSQLAlchemyを使用している場合は、継承階層をデータベース テーブルにマップする方法を構成できます。オブジェクト リレーショナル マッパーは、退屈な SQL を使いこなすのに適しています。

あなたの問題は、垂直テーブルに適している可能性があります。すべてをスキーマに格納する代わりに、オブジェクトのタイプと主キーを 1 つのテーブルに格納し、各オブジェクトのキーと値のタプルを別のテーブルに格納します。実際に車のテストを保存している場合、このセットアップにより、新しい種類の結果を簡単に追加できます。

于 2009-02-16T21:59:39.667 に答える
-1

「gen-spec リレーショナル モデリング」で Google 検索を行います。一般化されたエンティティ (OO プログラマーがスーパークラスと呼ぶもの) の属性を格納するテーブルを設定する方法、特殊なエンティティ (サブクラス) ごとに個別のテーブルを設定する方法、外部キーを使用してそれをリンクする方法に関する記事を見つけることができます。すべて一緒に。

最高の記事である IMO では、ER モデリングの観点から gen-spec について説明しています。ER モデルをリレーショナル モデルに変換し、そこから SQL テーブルに変換する方法を知っている場合は、ER で gen-spec をモデル化する方法が示されたら、何をすべきかがわかります。

「gen-spec」でグーグル検索すると、表示されるもののほとんどはリレーショナル指向ではなくオブジェクト指向です。オブジェクトのリレーショナルインピーダンスの不一致を克服する方法を知っている限り、そのようなものも役立つかもしれません。

于 2009-02-16T21:58:45.193 に答える
-3

あなたの設計は合理的であり、正しい正規化規則に従っています。Vehicle Id と Type を含む Vehicle テーブルがない可能性があります (つまり、Speedboats、Cars、および Gokarts の「親」...「DesignedByUserId」のようなものを保持します)。Vehicle テーブルと Speedboats テーブルの間には 1 対 1 の関係があり、Vehicle と Speedboat/Cars/GoKarts の間には 1 対 1 の関係があります (つまり、車両は speedboat のレコードを 1 つだけ持つことができます。車またはゴーカート)... ただし、ほとんどのデータベースでは、これを簡単に強制するメカニズムは提供されていません。

このような種類のものを識別するのに役立つ正規化規則の 1 つは、フィールドはテーブルの主キーのみに依存する必要があるということです。スピードボート、車、ゴーカートのテスト結果が一緒に保存される統合テーブルでは、車関連のフィールドはテスト日だけでなく、車両 ID と車両タイプにも依存します。テスト結果テーブルの主キーはテスト日 + 車両 ID であり、テスト データ行を一意にするのは車両タイプではありません (つまり、1 台の特定の車両で 2009 年 1 月 1 日 12:30 にテストを実施する必要があります)。それはスピードボートと車の両方です...いいえ...できません)。

特に正規化規則をうまく説明しているわけではありませんが、形式的な説明を読むと、3次/4次/5次正規形の規則はいつも混乱します。それらの 1 つ (3 番目/4 番目/5 番目) は、主キーと主キーのみに依存するフィールドを処理します。このルールは、主キーが正しく識別されていることを前提としています (主キーを誤って定義するのは非常に簡単です)。

于 2009-02-16T21:19:13.743 に答える