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
.
поэтому я предлагаю второй подход, т. е. использование внешнего ключа в качестве первичного ключа. Это:
- использует один столбец меньше
- позволяет однозначно идентифицировать каждую строку
- позволяет сопоставить учетную запись с пользователем
как насчет следующего подхода
-
создать таблицу user
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
создать учетную запись в таблицу с уникальным индексом на
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;
-
создать таблицу 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 сначала удалить сопоставление.