検討中の事業環境のあなたの説明によると、そこに存在するスーパータイプサブタイプを包含する構造項目 -the supertype-とそのそれぞれのカテゴリー、すなわち、車、ボートや飛行機は(2とともにより知らされていませんでした) -サブタイプ—。
そのようなシナリオを管理するために私が従う方法について、以下で詳しく説明します。
ビジネスルール
関連する概念スキーマの説明を始めるために、これまでに決定されたいくつかの最も重要なビジネスルール(分析を3つの開示されたカテゴリのみに制限し、できるだけ簡潔にする)を次のように定式化できます。
- ユーザーは、ゼロ・ワン・オア・多く所有しているアイテムを
- 項目は、正確に-1によって所有され、ユーザーの特定の瞬間に
- 項目は、 1対多によって所有されるユーザー時間に明確なポイントで
- 項目は正確に一つによって分類されているカテゴリー
- 項目は、すべての回で、あります
例示的なIDEF1Xダイアグラム
図1は、前の定式化をグループ化するために作成したIDEF1X 1ダイアグラムと、関連する他のビジネスルールを示しています。
スーパータイプ
一方、スーパータイプであるItemは、すべてのカテゴリに共通のプロパティ†または属性を示します。
- CategoryCodeを参照のことFOREIGN KEY(FK)として-specified Category.CategoryCodeサブタイプ等の機能識別器、すなわち、それは正確示すカテゴリ所与れるサブタイプのアイテムは、connected-なければならないし
- OwnerIdにはポイントとFKとして-distinguished User.UserIdが、私はそれ役割名割り当てられた2、その特別な意味合いがよりaccurately-反映させるために、
- フー、
- バー、
- バズと
- CreatedDateTime。
サブタイプ
一方、プロパティが‡すべての特定に関係することをカテゴリー、すなわち、
- QuxとCorge ;
- Grault、Garply、およびPlugh ;
- XYZZY、ドサッ、WibbleとFlob。
対応するサブタイプボックスに表示されます。
識別子
次に、Item.ItemId PRIMARY KEY(PK)は、3を異なる役割名を持つサブタイプに移行しました。
相互に排他的な関連付け
図示されているように、(a)各スーパータイプの出現と(b)その相補的なサブタイプのインスタンスとの間には、カーディナリティが1対1(1:1)の関連または関係がある。
排他的なサブタイプのシンボルはサブタイプ、すなわち、相互に排他的であるという事実を描いて、具体的なアイテムの発生は、単一のサブタイプのインスタンスによって補完することができますいずれかの車、または1 面、または1 ボート(決して2以上)。
†、‡実際の金種が質問に含まれていないため、一部のエンティティタイププロパティに資格を付与するために、クラシックプレースホルダー名を使用しました。
説明論理レベルのレイアウト
したがって、説明的な論理設計について説明するために、上記で表示および説明されたIDEF1Xダイアグラムに基づいて、次のSQL-DDLステートメントを導出しました。
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business context.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE UserProfile (
UserId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATE NOT NULL,
GenderCode CHAR(3) NOT NULL,
Username CHAR(20) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- ALTERNATE KEY.
);
CREATE TABLE Category (
CategoryCode CHAR(1) NOT NULL, -- Meant to contain meaningful, short and stable values, e.g.; 'C' for 'Car'; 'B' for 'Boat'; 'P' for 'Plane'.
Name CHAR(30) NOT NULL,
--
CONSTRAINT Category_PK PRIMARY KEY (CategoryCode),
CONSTRAINT Category_AK UNIQUE (Name) -- ALTERNATE KEY.
);
CREATE TABLE Item ( -- Stands for the supertype.
ItemId INT NOT NULL,
OwnerId INT NOT NULL,
CategoryCode CHAR(1) NOT NULL, -- Denotes the subtype discriminator.
Foo CHAR(30) NOT NULL,
Bar CHAR(30) NOT NULL,
Baz CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Item_PK PRIMARY KEY (ItemId),
CONSTRAINT Item_to_Category_FK FOREIGN KEY (CategoryCode)
REFERENCES Category (CategoryCode),
CONSTRAINT Item_to_User_FK FOREIGN KEY (OwnerId)
REFERENCES UserProfile (UserId)
);
CREATE TABLE Car ( -- Represents one of the subtypes.
CarId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Qux CHAR(30) NOT NULL,
Corge CHAR(30) NOT NULL,
--
CONSTRAINT Car_PK PRIMARY KEY (CarId),
CONSTRAINT Car_to_Item_FK FOREIGN KEY (CarId)
REFERENCES Item (ItemId)
);
CREATE TABLE Boat ( -- Stands for one of the subtypes.
BoatId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Grault CHAR(30) NOT NULL,
Garply CHAR(30) NOT NULL,
Plugh CHAR(30) NOT NULL,
--
CONSTRAINT Boat_PK PRIMARY KEY (BoatId),
CONSTRAINT Boat_to_Item_FK FOREIGN KEY (BoatId)
REFERENCES Item (ItemId)
);
CREATE TABLE Plane ( -- Denotes one of the subtypes.
PlaneId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Xyzzy CHAR(30) NOT NULL,
Thud CHAR(30) NOT NULL,
Wibble CHAR(30) NOT NULL,
Flob CHAR(30) NOT NULL,
--
CONSTRAINT Plane_PK PRIMARY KEY (PlaneId),
CONSTRAINT Plane_to_Item_PK FOREIGN KEY (PlaneId)
REFERENCES Item (ItemId)
);
実例で示されているように、スーパーエンティティタイプと各サブエンティティタイプは、対応するベーステーブルで表されます。
列CarId
、BoatId
およびPlaneId
適切なテーブルのPK類として拘束、FK制約の方法によって概念レベル一対一の関連を表すのヘルプ§と点ItemId
のPKとして拘束されている列、Item
テーブル。これは、実際の「ペア」では、スーパータイプ行とサブタイプ行の両方が同じPK値によって識別されることを意味します。したがって、それは、
- ()取付余分ホールドシステム制御サロゲート値に列‖サブタイプ放置(b)のテーブルには(C)で完全に余分。
§(特にFOREIGN)KEY制約の定義(コメントで参照した状況)に関する問題やエラーを防止するために、以下に例示するように、手元のさまざまなテーブル間で発生する存在依存性を考慮することが非常に重要です。このSQL Fiddleでも提供した、説明用DDL構造内のテーブルの宣言順序。
‖たとえば、MySQLで構築されたデータベースのテーブルにAUTO_INCREMENTプロパティを含む追加の列を追加します。
整合性と一貫性に関する考慮事項
ビジネス環境では、(1)「スーパータイプ」の各行が常に対応する「サブタイプ」の対応物によって補完されていることを確認する必要があることを指摘することが重要です。 「サブタイプ」行は、「スーパータイプ」行の「弁別子」列に含まれる値と互換性があります。
宣言的な方法でこのような状況を強制することは非常にエレガントですが、残念ながら、私が知る限り、主要なSQLプラットフォームのいずれも、そのための適切なメカニズムを提供していません。したがって、ACID TRANSACTIONS内の手続き型コードを使用すると、データベースでこれらの条件が常に満たされるので非常に便利です。他のオプションはトリガーを使用することですが、いわば、物事を乱雑にする傾向があります。
有用なビューの宣言
上記で説明したような論理的な設計がある場合、1つ以上のビュー、つまり2つ以上の関連するベーステーブルに属する列を含む派生テーブルを作成することは非常に実用的です。このようにして、たとえば、「結合された」情報を取得する必要があるたびにすべてのJOINを書き込む必要なく、これらのビューから直接SELECTできます。
サンプルデータ
この点で、ベーステーブルには以下に示すサンプルデータが「入力」されているとしましょう。
--
INSERT INTO UserProfile
(UserId, FirstName, LastName, BirthDate, GenderCode, Username, CreatedDateTime)
VALUES
(1, 'Edgar', 'Codd', '1923-08-19', 'M', 'ted.codd', CURDATE()),
(2, 'Michelangelo', 'Buonarroti', '1475-03-06', 'M', 'michelangelo', CURDATE()),
(3, 'Diego', 'Velázquez', '1599-06-06', 'M', 'd.velazquez', CURDATE());
INSERT INTO Category
(CategoryCode, Name)
VALUES
('C', 'Car'), ('B', 'Boat'), ('P', 'Plane');
-- 1. ‘Full’ Car INSERTion
-- 1.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(1, 1, 'C', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 1.2
INSERT INTO Car
(CarId, Qux, Corge)
VALUES
(1, 'Fantastic Car', 'Powerful engine pre-update!');
-- 2. ‘Full’ Boat INSERTion
-- 2.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(2, 2, 'B', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 2.2
INSERT INTO Boat
(BoatId, Grault, Garply, Plugh)
VALUES
(2, 'Excellent boat', 'Use it to sail', 'Everyday!');
-- 3 ‘Full’ Plane INSERTion
-- 3.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(3, 3, 'P', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 3.2
INSERT INTO Plane
(PlaneId, Xyzzy, Thud, Wibble, Flob)
VALUES
(3, 'Extraordinary plane', 'Traverses the sky', 'Free', 'Like a bird!');
--
次に、有利なビューは、一からギャザー列でありItem
、Car
そしてUserProfile
:
--
CREATE VIEW CarAndOwner AS
SELECT C.CarId,
I.Foo,
I.Bar,
I.Baz,
C.Qux,
C.Corge,
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Car C
ON C.CarId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
当然のことながら、同様のアプローチに従って、1つのテーブル(これらの場合は派生テーブル)Boat
から「完全な」Plane
情報を直接SELECTすることもできます。
あなた-ifそれは、次のビュー定義で結果セット-でNULLマークの有無については気にしないの後、次のことができ、例えば、「コレクト」のテーブルの列Item
、Car
、Boat
、Plane
とUserProfile
:
--
CREATE VIEW FullItemAndOwner AS
SELECT I.ItemId,
I.Foo, -- Common to all Categories.
I.Bar, -- Common to all Categories.
I.Baz, -- Common to all Categories.
IC.Name AS Category,
C.Qux, -- Applies to Cars only.
C.Corge, -- Applies to Cars only.
--
B.Grault, -- Applies to Boats only.
B.Garply, -- Applies to Boats only.
B.Plugh, -- Applies to Boats only.
--
P.Xyzzy, -- Applies to Planes only.
P.Thud, -- Applies to Planes only.
P.Wibble, -- Applies to Planes only.
P.Flob, -- Applies to Planes only.
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Category IC
ON I.CategoryCode = IC.CategoryCode
LEFT JOIN Car C
ON C.CarId = I.ItemId
LEFT JOIN Boat B
ON B.BoatId = I.ItemId
LEFT JOIN Plane P
ON P.PlaneId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
ここに示されているビューのコードは、単なる例示です。もちろん、いくつかのテスト演習や変更を行うと、手元にあるクエリの(物理的な)実行を加速するのに役立つ場合があります。さらに、ビジネスニーズに応じて、上記のビューから列を削除または追加する必要がある場合があります。
サンプルデータとすべてのビュー定義は、このSQL Fiddleに組み込まれているため、「動作中」で観察できます。
データ操作:アプリケーションプログラムのコードと列のエイリアス
アプリケーションプログラムコードの使用(「サーバー側の特定のコード」で意味する場合)と列のエイリアスは、次のコメントで取り上げた他の重要なポイントです。
サーバー側の特定のコードで[JOIN]の問題を回避することはできましたが、本当にしたくありません。そして、すべての列にエイリアスを追加すると、「ストレス」になる可能性があります。
非常によく説明されています、ありがとうございました。ただし、複数のエイリアスを使用してステートメントを簡潔にしたくないため、一部の列との類似性のため、すべてのデータを一覧表示するときに、結果セットを操作する必要があると思いました。
アプリケーションプログラムコードを使用することは、結果セットのプレゼンテーション(またはグラフィカル)機能を処理するのに非常に適しているリソースですが、実行速度の問題を防ぐには、行ごとのデータ検索を回避することが最も重要であることを示すのは適切です。目的は、システムの動作を最適化できるように、SQLプラットフォームの(正確に)セットエンジンによって提供される堅牢なデータ操作ツールを使用して、関連するデータセットを「フェッチ」することです。
さらに、エイリアスを使用して特定のスコープ内の1つ以上の列の名前を変更することはストレスを感じるように見えるかもしれませんが、個人的には、(i)コンテキスト化し、(ii)関連することによる意味と意図を明確にするのに役立つ非常に強力なツールであると思います列; したがって、これは関心のあるデータの操作に関して完全に熟考されるべき側面です。
同様のシナリオ
相互に排他的なサブタイプとのスーパータイプとサブタイプの関連付けを含む他の2つのケースに対する私の見解を含む、この一連の投稿とこの投稿のグループのヘルプを見つけることもできます。
私はまた、サブタイプがこの(新しい)回答で相互に排他的ではないスーパータイプサブタイプクラスターを含むビジネス環境のソリューションを提案しました。
文末脚注
1 情報モデリングの統合定義( IDEF1X)は、1993年12月に米国国立標準技術研究所(NIST)によって標準として確立された、非常に推奨されるデータモデリング手法です。これは、(a)リレーショナルモデルの唯一の創始者、つまり EF Codd博士によって作成された理論的著作のいくつかにしっかりと基づいています。(b) PP Chen博士によって作成されたエンティティ関係ビュー。また、(c)Robert G. Brownが作成した論理データベース設計手法についても説明します。
2 IDEF1Xでは、ロール名は、それぞれのエンティティタイプのスコープ内で保持する意味を表すために、FKプロパティ(または属性)に割り当てられる独特のラベルです。
3 IDEF1X標準では、キーの移行を「親エンティティまたは汎用エンティティの主キーを、その子エンティティまたはカテゴリエンティティに外部キーとして配置するモデリングプロセス」と定義しています。
Item
テーブルにはCategoryCode
列が含まれています。「完全性と一貫性に関する考慮事項」というタイトルのセクションで述べたように、