Учтите следующее:
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; а именно, что оба типа пользователей являются пользователями. Теперь есть несколько способов представить это в РСУБД:
Первый путь
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)
)
Второй способ
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
)
Третий путь
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)
)
Я почти уверен, что третий путь - неправильный, потому что невозможно выполнить простое объединение с пользователями в другом месте базы данных.
Мой реальный пример - это не пользователи с другим логином; Меня интересует, как смоделировать эти отношения в общем случае.
database-design
Билли ОНил
источник
источник
Ответы:
Второй способ - правильный путь.
Ваш базовый класс получает таблицу, а затем дочерние классы получают свои собственные таблицы только с дополнительными полями, которые они вводят, плюс ссылки внешнего ключа на базовую таблицу.
Как предложил Джоэл в своих комментариях к этому ответу, вы можете гарантировать, что у пользователя будет либо прямой вход в систему, либо вход в систему OpenID, но не оба (и, возможно, ни тот, ни другой), добавив столбец типа в каждую таблицу подтипов, которая возвращает ключи к корневой таблице. Столбец типа в каждой таблице подтипов ограничен одним значением, представляющим тип этой таблицы. Поскольку этот столбец имеет внешний ключ к корневой таблице, только одна строка подтипа может одновременно ссылаться на одну и ту же корневую строку.
Например, MySQL DDL будет выглядеть примерно так:
(На других платформах вы бы использовали
CHECK
ограничение вместоENUM
.) MySQL поддерживает составные внешние ключи, так что это должно работать для вас.NULL
Первый способ действителен, хотя вы тратите пространство в этих столбцах -able, потому что их использование зависит от типа пользователя. Преимущество состоит в том, что если вы решите расширить, какие типы пользовательских типов хранить, и эти типы не требуют дополнительных столбцов, вы можете просто расширить свой доменENUM
и использовать ту же таблицу.Третий способ заставляет любые запросы, ссылающиеся на пользователей, проверять обе таблицы. Это также не позволяет вам ссылаться на таблицу одного пользователя с помощью внешнего ключа.
источник
UNION
, или я бы предложил индексированное представление с уникальным индексом противUNION ALL
изuid
из двух таблиц.)type
столбец к каждой таблице подтипов, который ограничен посредствомCHECK
ограничения, чтобы иметь ровно одно значение (тип этой таблицы). Затем мы превращаем внешние ключи вложенной таблицы в супер-таблицу в составные ключи на обоихuid
иtype
. Это гениально.Они будут названы
и все они имеют законное использование и поддерживаются некоторыми библиотеками. Вы должны выяснить, что подходит лучше всего.
Наличие нескольких таблиц сделает управление данными в коде вашего приложения более понятным, но уменьшит объем неиспользуемого пространства.
источник