IS-A関係をデータベースにマップするにはどうすればよいですか?


26

以下を考慮してください。

entity User
{
    autoincrement uid;
    string(20) name;
    int privilegeLevel;
}

entity DirectLoginUser
{
    inherits User;
    string(20) username;
    string(16) passwordHash;
}

entity OpenIdUser
{
    inherits User;
    //Whatever attributes OpenID needs... I don't know; this is hypothetical
}

さまざまな種類のユーザー(直接ログインユーザー、およびOpenIDユーザー)がIS-A関係を表示します。つまり、両方のタイプのユーザーがユーザーです。現在、これをRDBMSで表す方法はいくつかあります。

ウェイワン

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    username VARCHAR(20) NULL,
    passwordHash VARCHAR(20) NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

ウェイ2

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privilegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE DirectLogins
(
    uid INTEGER NOT_NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

CREATE TABLE OpenIDLogins
(
    uid INTEGER NOT_NULL,
    // ...
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

ウェイ3

CREATE TABLE DirectLoginUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE OpenIDUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

データベース内の他の場所のユーザーに対して単純な結合を行うことはできないため、3番目の方法が間違った方法であることはほぼ確実です。

私の実世界の例は、異なるログインを持つユーザーの例ではありません。一般的なケースでこの関係をモデル化する方法に興味があります。


回答を編集して、Joel Brownのコメントで提案されているアプローチを含めました。それはあなたのために働くはずです。さらに提案が必要な場合は、質問にタグを付け直して、MySQL固有の回答を探していることを示します。
ニックチャマス

データベースの残りの部分でリレーションがどのように使用されるかに本当に依存することに注意してください。子エンティティが関係する関係では、それらのテーブルのみへの外部キーが必要であり、何らかのハッキングなしで単一の統合テーブルへのマッピングが禁止されます。
beldaz

PostgreSQLのようなオブジェクトリレーショナルデータベースには、実際には4番目の方法があります。別のテーブルからテーブルINHERITSを宣言できます。postgresql.org/docs/current/static/tutorial-inheritance.html
MarkusSchaber

回答:


16

方法2は正しい方法です。

基本クラスはテーブルを取得し、その後、子クラスは、導入する追加フ​​ィールドだけで、さらに基本テーブルへの外部キー参照を含む独自のテーブルを取得します。

ジョエルはこの答えに彼のコメントで提案されている、あなたはキーバックすることを各サブタイプのテーブルにタイプの列を追加することにより、ユーザーが直接ログインまたはOpenIDでのログインのいずれかを持っていますが、両方ではない(ともおそらくどちらも)することを保証することができますルートテーブルに。各サブタイプテーブルのタイプ列は、そのテーブルのタイプを表す単一の値を持つように制限されています。この列はルートテーブルへの外部キーであるため、一度に1つのサブタイプ行のみが同じルート行にリンクできます。

たとえば、MySQL DDLは次のようになります。

CREATE TABLE Users
(
      uid               INTEGER AUTO_INCREMENT NOT NULL
    , type              ENUM("DirectLogin", "OpenID") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
);

CREATE TABLE DirectLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("DirectLogin") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
    , FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

CREATE TABLE OpenIDLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("OpenID") NOT NULL
    // ...

    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

(他のプラットフォームCHECKでは、ENUM。の代わりに制約を使用します。)MySQL 複合外部キーをサポートしているため、これが機能します。

方法1は有効ですが、これらのNULL列の使用はユーザーのタイプに依存するため、これらの列のスペースを無駄にしています。利点は、格納するユーザータイプの種類を拡張することを選択し、それらのタイプが追加の列を必要としない場合、ドメインを拡張しENUMて同じテーブルを使用できることです。

方法3は、ユーザーを参照するクエリを強制的に両方のテーブルに対してチェックします。これにより、外部キーを介して単一のユーザーテーブルを参照することもできなくなります。


1
方法2を使用して、他の2つのテーブルに対応する行が1つだけ存在することを強制する方法がないという事実に対処するにはどうすればよいですか?
ビリーONeal

2
@ビリー-良い反論。ユーザーがどちらか一方しか持てない場合は、procレイヤーまたはトリガーを介してこれを実施できます。この制約を強制するDDLレベルの方法があるのだろうか。(ああ、インデックス付きビューが許可されていない UNION、または私はに対して一意のインデックスとインデックス付きビューを示唆していたUNION ALLuid二つのテーブルからを。)
ニックChammas

もちろん、最初にRDBMSがインデックス付きビューをサポートしていることを前提としています。
ビリーONeal

1
この種のクロステーブル制約を実装する便利な方法は、スーパータイプのテーブルにパーティション属性を含めることです。次に、各サブタイプは、適切なパーティション属性値を持つスーパータイプのみに関連することを確認するためにチェックできます。これにより、1つ以上の他のサブタイプテーブルを調べることにより、衝突テストを行う必要がなくなります。
ジョエルブラウン

1
@Joel-したがって、たとえば、制約をtype介しCHECKて厳密に1つの値(そのテーブルのタイプ)を持つように制限されている各サブタイプテーブルに列を追加します。次に、スーパーテーブルへのサブテーブル外部キーをuidとの両方で複合キーにしますtype。それは独創的です。
ニックチャマス

5

彼らは名前が付けられます

  1. 単一テーブルの継承
  2. クラステーブルの継承
  3. 具体的なテーブルの継承

また、すべてに正当な用途があり、一部のライブラリでサポートされています。どちらが最適かを調べる必要があります。

複数のテーブルがあると、データ管理がアプリケーションコードにより多くかかりますが、未使用の領域の量が減ります。


2
「共有主キー」と呼ばれる追加のテクニックがあります。この手法では、サブクラステーブルには、プライマリキーIDが個別に割り当てられていません。代わりに、サブクラステーブルのPKは、スーパークラステーブルを参照するFKです。これには、主にIS-A関係の1対1の性質を強制するといういくつかの利点があります。この手法は、クラステーブルの継承に追加されます。
ウォルターミッティ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.