web-dev-qa-db-fra.com

MySQL - Relation un à un?

J'essaie de réaliser une relation un à un dans la base de données MySQL. Par exemple, supposons que j'ai une table Utilisateurs et une table Comptes. Et je veux être sûr que l'utilisateur ne peut avoir qu'un seul compte. Et qu'il ne peut y avoir qu'un seul compte par utilisateur.

J'ai trouvé deux solutions pour cela, mais je ne sais pas quoi utiliser, et y a-t-il d'autres options.

Première solution:

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;

Dans cet exemple, je définis la clé étrangère dans les comptes pointant vers la clé primaire dans les utilisateurs. Et puis je rend la clé étrangère UNIQUE, donc il ne peut pas y avoir deux utilisateurs identiques dans les comptes. Pour joindre des tables, j'utiliserais cette requête:

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

Deuxième solution:

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;

Dans cet exemple, je crée une clé étrangère qui pointe de la clé primaire vers une clé primaire dans une autre table. Étant donné que les clés primaires sont UNIQUES par défaut, cela rend cette relation un à un. Pour rejoindre des tables, je peux utiliser ceci:

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

Maintenant les questions:

  • Quelle est la meilleure façon de créer une relation One to One dans MySQL?
  • Y a-t-il d'autres solutions que ces deux-là?

J'utilise MySQL Workbench, et lorsque je conçois une relation un à un dans le diagramme EER et que MySQL Workbench produit du code SQL, j'obtiens une relation un à plusieurs: S C'est ce qui m'embrouille: S

Et si j'importe l'une de ces solutions dans le diagramme EER de MySQL Workbench, il reconnaît les relations comme un à plusieurs: S C'est aussi déroutant.

Alors, quelle serait la meilleure façon de définir une relation One to One dans MySQL DDL. Et quelles sont les options pour y parvenir?

51
Limeni

Étant donné que les clés primaires sont UNIQUES par défaut, cela rend cette relation un à un.

Non, cela fait la relation "un à zéro ou un". Est-ce vraiment ce dont vous avez besoin?

Si oui , alors votre "deuxième solution" est meilleure:

  • c'est plus simple,
  • prend moins de stockage1 (et rend donc le cache "plus grand")
  • hes moins d'index à maintenir2, ce qui profite à la manipulation des données,
  • et (puisque vous utilisez InnoDB) naturellement clusters les données, donc les utilisateurs qui sont proches verront leurs comptes également rapprochés, ce qui peut bénéficier à la localité du cache et à certains types d'analyses de plage.

BTW, vous devrez faire accounts.id un entier ordinaire (pas d'incrémentation automatique) pour que cela fonctionne.

Si non , voir ci-dessous ...

Quelle est la meilleure façon de créer une relation One to One dans MySQL?

Eh bien, "le meilleur" est un Word surchargé, mais la solution "standard" serait la même que dans n'importe quelle autre base de données: placez les deux entités (utilisateur et compte dans votre cas) dans la même table physique.

Y a-t-il d'autres solutions que ces deux-là?

Théoriquement, vous pourriez créer des FK circulaires entre les deux PK, mais cela nécessiterait des contraintes différées pour résoudre le problème du poulet et des œufs, qui ne sont malheureusement pas pris en charge sous MySQL.

Et si j'importe l'une de ces solutions dans le diagramme EER de MySQL Workbench, il reconnaît les relations comme un à plusieurs: S C'est aussi déroutant.

Je n'ai pas beaucoup d'expérience pratique avec cet outil de modélisation particulier, mais je suppose que c'est parce que c'est "un à plusieurs" où le côté "plusieurs" a été limité à 1 en le rendant unique. N'oubliez pas que "plusieurs" ne signifie pas "1 ou plusieurs", cela signifie "0 ou plusieurs", donc la version "plafonnée" signifie vraiment "0 ou 1".


1 Pas seulement dans les frais de stockage pour le champ supplémentaire, mais aussi pour l'index secondaire. Et puisque vous utilisez InnoDB qui toujours les tables en cluster , sachez que les index secondaires sont encore plus chers dans les tables en cluster que dans les tables basées sur le tas.

2 InnoDB nécessite des index sur les clés étrangères.

40

Votre première approche crée deux clés candidates dans la table des comptes: id et user_id.

Je suggère donc la deuxième approche, c'est-à-dire l'utilisation de la clé étrangère comme clé primaire. Cette:

  • utilise une colonne de moins
  • vous permet d'identifier de manière unique chaque ligne
  • vous permet de faire correspondre le compte à l'utilisateur
8
Salman A