65

MySQL データベースで「1 対 1」の関係を実現しようとしています。たとえば、Users テーブルと Accounts テーブルがあるとします。ユーザーが持つことができるアカウントは 1 つだけであることを確認したいと思います。また、ユーザーごとに 1 つのアカウントしか存在できないこと。

これには 2 つの解決策が見つかりましたが、何を使用すればよいかわかりません。他のオプションはありますか。

最初の解決策:

DROP DATABASE IF EXISTS test;
CREATE DATABASE test CHARSET = utf8 COLLATE = utf8_general_ci;
USE test;

CREATE TABLE users(
    id INT NOT NULL AUTO_INCREMENT,
    user_name VARCHAR(45) NOT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

CREATE TABLE accounts(
    id INT NOT NULL AUTO_INCREMENT,
    account_name VARCHAR(45) NOT NULL,
    user_id INT UNIQUE,
    PRIMARY KEY(id),
    FOREIGN KEY(user_id) REFERENCES users(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

この例では、users の主キーを指す accounts の外部キーを定義します。次に、外部キーを UNIQUE にするため、アカウントに同一のユーザーが 2 人存在することはありません。テーブルを結合するには、次のクエリを使用します。

SELECT * FROM users JOIN accounts ON users.id = accounts.user_id;

2番目の解決策:

DROP DATABASE IF EXISTS test;
CREATE DATABASE test CHARSET = utf8 COLLATE = utf8_general_ci;
USE test;

CREATE TABLE users(
    id INT NOT NULL AUTO_INCREMENT,
    user_name VARCHAR(45) NOT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

CREATE TABLE accounts(
    id INT NOT NULL AUTO_INCREMENT,
    account_name VARCHAR(45) NOT NULL,
    PRIMARY KEY(id),
    FOREIGN KEY(id) REFERENCES users(id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8;

この例では、主キーから別のテーブルの主キーを指す外部キーを作成します。主キーはデフォルトで UNIQUE であるため、この関係は 1 対 1 になります。テーブルを結合するには、これを使用できます。

SELECT * FROM users JOIN accounts ON users.id = accounts.id;

今質問:

  • MySQL で 1 対 1 の関係を作成する最良の方法は何ですか?
  • この2つ以外に解決策はありますか?

私はMySQL Workbenchを使用しています.EERダイアグラムで1対1の関係を設計し、MySQL WorkbenchにSQLコードを生成させると、1対多の関係が得られます:Sそれが私を混乱させるものです:S

そして、これらのソリューションのいずれかを MySQL Workbench EER ダイアグラムにインポートすると、関係が 1 対多として認識されます :S これもややこしいです。

では、MySQL DDL で 1 対 1 の関係を定義する最良の方法は何でしょうか。そして、これを達成するためにどのようなオプションがありますか?

4

3 に答える 3

56

主キーはデフォルトで一意であるため、これによりこの関係は1対1になります。

いいえ、それは関係を「1対0または1」にします。それはあなたが実際に必要なものですか?

はいの場合、「2番目の解決策」の方が優れています。

  • 簡単です、
  • 必要なストレージ1が少なくなります(したがって、キャッシュが「大きく」なります)
  • データ操作に役立つ2を維持するためのインデックスが少なくなります。
  • (InnoDBを使用しているため)自然にデータをクラスター化するため、近くにいるユーザーはアカウントも近くに保存され、キャッシュの局所性と特定の種類の範囲スキャンに役立つ可能性があります。

accounts.idところで、これを機能させるには、通常の整数(自動インクリメントではない)を作成する必要があります。

いいえの場合は、以下を参照してください...

MySQLで1対1の関係を作成するための最良の方法は何ですか?

「最良」はオーバーロードされた単語ですが、「標準」ソリューションは他のデータベースと同じです。両方のエンティティ(この場合はユーザーとアカウント)を同じ物理テーブルに配置します。

これら2つ以外の解決策はありますか?

理論的には、2つのPK間で循環FKを作成できますが、残念ながらMySQLではサポートされていない鶏が先か卵が先かという問題を解決するために遅延制約が必要になります。

そして、これらのソリューションのいずれかをMySQL Workbench EERダイアグラムにインポートすると、関係が1対多:Sとして認識されます。これも混乱を招きます。

その特定のモデリングツールについてはあまり実用的な経験はありませんが、それは「多」側をユニークにすることで「多」側を1に制限した「1対多」だからだと思います。「多」は「1または多」を意味するのではなく、「0または多」を意味するため、「上限付き」バージョンは実際には「0または1」を意味することに注意してください。


1追加フィールドの保管費用だけでなく、セカンダリインデックスの保管費用も含まれます。また、常にテーブルをクラスター化するInnoDBを使用しているため、クラスター化テーブルでは、ヒープベースのテーブルよりもセカンダリインデックスの方がコストがかかることに注意してください。

2 InnoDBには、外部キーのインデックスが必要です。

于 2012-11-30T12:44:22.930 に答える
9

最初のアプローチでは、accounts テーブルにidとの 2 つの候補キーが作成されますuser_id

したがって、外部キーを主キーとして使用する 2 番目のアプローチをお勧めします。これ:

  • 使用する列が 1 つ少ない
  • 各行を一意に識別できます
  • アカウントとユーザーを一致させることができます
于 2012-11-30T12:17:00.103 に答える