MySQL-Отношение Один К Одному?

теперь я знаю, что есть некоторые ответы об одном отношении к одному, но ни один из них не ответил на мой вопрос, поэтому, пожалуйста, прочитайте это перед голосованием:)

Im пытается достичь отношения один к одному в базе данных MySQL. Например, скажем, у меня есть таблица Users и таблица Accounts. И я хочу быть уверен, что пользователь может иметь только одну учетную запись. И что на каждого пользователя может быть только одна учетная запись.

Я нашел два решения для этого, но не знаю, что использовать, и есть ли любой другой вариант.

первый вариант:

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;

в этом примере я определяю внешний ключ в учетных записях, указывающих на первичный ключ в пользователях. И затем я делаю внешний ключ уникальным, поэтому в учетных записях не может быть двух одинаковых пользователей. Чтобы присоединиться к таблицам, я бы использовал этот запрос:

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

второй вариант:

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;

в этом примере я создаю внешний ключ, который указывает от первичного ключа на первичный ключ в другой таблице. Поскольку первичные ключи уникальны по умолчанию это делает это отношение один к одному. Чтобы присоединиться к таблицам, я могу использовать это:

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

теперь вопросы:

  • каков наилучший способ создания отношения один к одному в MySQL?
  • есть ли другие решения, кроме этих двух?

Я использую MySQL Workbench, и когда я разрабатываю одно к одному отношение в диаграмме EER, и пусть MySQL Workbench производит SQL-код, я получаю одно ко многим отношениям :вот что меня смущает : S

и если я импортирую любое из этих решений в диаграмму MySQL Workbench EER, он распознает отношения как один ко многим :это также запутывает.

Итак, каков был бы лучший способ определить отношение один к одному в MySQL DDL. И какие есть варианты, чтобы достичь этого?

спасибо!!

3 ответов


поскольку первичные ключи по умолчанию уникальны, это делает это отношение один к одному.

нет, что делает отношение "один к нулю или одному". Это то, что тебе действительно нужно?

Если да, тогда ваше "второе решение" лучше:

  • это проще,
  • занимает меньше места1 (и поэтому делает кэш "большим")
  • он меньше индексов для поддержания2, что выгодно для манипулирования данными,
  • и (так как вы используете InnoDB) естественно кластеров данные, поэтому пользователи, которые близки друг к другу, будут хранить свои учетные записи близко друг к другу, что может принести пользу локальности кэша и определенным видам сканирования диапазона.

кстати, вам нужно сделать accounts.id обычное целое число (не автоматическое приращение) для этого.

Если нет см. ниже...

что лучший способ создать отношение один к одному в MySQL?

Ну, "лучший" - это перегруженное слово, но" стандартное " решение будет таким же, как и в любой другой базе данных: поместите оба объекта (пользователя и учетную запись в вашем случае) в одну физическую таблицу.

есть ли другие решения, кроме этих двух?

теоретически вы можете сделать круговой FKs между двумя PKs, но для этого потребуется отложить ограничения для решения проблемы курицы и яйца, которые, к сожалению, не поддерживаются в MySQL.

и если я импортирую любое из этих решений в диаграмму MySQL Workbench EER, он распознает отношения как один ко многим :это также запутывает.

у меня нет большого практического опыта работы с этим инструментом моделирования, но я предполагаю, что это потому, что это "один ко многим", где "много" стороны в пределах 1, сделав его уникальным. Пожалуйста, помните, что "многие" не означает "1 или много", это означает" 0 или много", поэтому" закрытая "версия действительно означает"0 или 1".


1 не только в расходах на хранение для дополнительного поля, но и для вторичного индекса. И поскольку вы используете InnoDB, который всегда кластеров, таблиц, остерегайтесь, что вторичные индексы еще дороже в кластеризованных таблицах, чем в таблицах на основе кучи.

2 InnoDB в требуются индексов на внешние ключи.


ваш первый подход создает два ключа-кандидата в таблице счета: id и user_id.

поэтому я предлагаю второй подход, т. е. использование внешнего ключа в качестве первичного ключа. Это:

  • использует один столбец меньше
  • позволяет однозначно идентифицировать каждую строку
  • позволяет сопоставить учетную запись с пользователем

как насчет следующего подхода

  1. создать таблицу user

    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(45) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
  2. создать учетную запись в таблицу с уникальным индексом на user_id и account_id С отношением внешнего ключа к пользователю / учетной записи и первичным ключом на user_id and account_id

    CREATE TABLE `account` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(45) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
  3. создать таблицу user2account

    CREATE TABLE `user2account` (
      `user_id` int(11) NOT NULL,
      `account_id` int(11) NOT NULL,
      PRIMARY KEY (`user_id`,`account_id`),
      UNIQUE KEY `FK_account_idx` (`account_id`),
      UNIQUE KEY `FK_user_idx` (`user_id`),
      CONSTRAINT `FK_account` FOREIGN KEY (`account_id`) REFERENCES         `account` (`id`),
      CONSTRAINT `FK_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

хотя это решение имеет самый большой след в базе данных, есть некоторые преимущества.

  • размещение FK_Key либо в таблице пользователей, либо в таблице учетных записей-это то, что я ожидаю от одного до многих релизов (у пользователя много учетных записей ...)
  • при этом user2account подход в основном используется для определения отношения "многие ко многим", добавляя уникальное ограничение на user_id и account_id предотвратит создание чего-то еще, кроме отношения "один к одному".

главное преимущество, которое я вижу в этом решении, заключается в том, что вы можете разделите работу в разных слоях кода или отделениях в компании

  • отдел A отвечает за создание пользователей, это возможно даже без разрешения на запись в таблицу учетных записей
  • Departement B отвечает за создание учетных записей, это возможно даже без разрешения на запись в пользовательскую таблицу
  • Departement C отвечает за создание сопоставления, это возможно даже без разрешения на запись в таблицу пользователя или учетной записи
  • после того, как Departement C создал сопоставление, ни пользователь, ни учетная запись не могут быть удалены departement A или B, не попросив departement C сначала удалить сопоставление.