Ответ 1
Я думаю, что @a1ex07 находится на правильном пути здесь (+1). Я не думаю, что пробелы в itemOrder
нарушают 3NF, но я беспокоюсь о другом нарушении 3NF (подробнее об этом ниже). Мы также должны следить за плохими данными в поле itemOrder
. Вот как бы я начал:
create table pages (
pid int,
primary key (pid)
);
create table users (
uid int,
primary key (uid)
);
create table items (
iid int,
primary key (iid)
);
create table details (
pid int not null references pages(pid),
uid int not null references users(uid),
iid int not null references items(iid),
itemOrder int,
primary key (pid, uid, iid),
unique (pid, uid, itemOrder)
);
Первичный ключ гарантирует, что для каждой страницы для каждого пользователя есть уникальные элементы. Уникальное ограничение гарантирует, что для каждой страницы для каждого пользователя есть уникальные элементы itemOrders. Здесь мое беспокойство о 3NF: в этом случае itemOrder
не полностью зависит от первичного ключа; это зависит только от частей (pid, uid)
. Это даже не 2NF; и что проблема. Мы могли бы включить itemOrder
в первичный ключ, но потом я беспокоюсь, что он может быть не минимальным, поскольку ПК должны быть. Возможно, нам придется разложить это на несколько таблиц. Еще думаю.,.
[РЕДАКТИРОВАТЬ - Больше размышлений на эту тему., ]
Предположения
-
Есть пользователи.
-
Есть страницы.
-
Есть элементы.
-
(страница, пользователь) идентифицирует SET элементов.
-
(страница, пользователь) идентифицирует упорядоченный список слотов, в котором мы можем хранить элементы, если хотите.
-
Мы не хотим дублировать элементы в списке (странице, пользователе).
План A
Убейте таблицу details
выше.
Добавьте таблицу ItemsByPageAndUser
, чтобы представить SET элементов, идентифицированных (страница, пользователь).
create table ItemsByPageAndUser (
pid int not null references pages(pid),
uid int not null references users(uid),
iid int not null references items(iid),
primary key (pid, uid, iid)
)
Добавить таблицу SlotsByPageAndUser
, чтобы представить упорядоченный список слотов, которые могут содержать элементы.
create table SlotsByPageAndUser (
pid int not null references pages(pid),
uid int not null references users(uid),
slotNum int not null,
iidInSlot int references items(iid),
primary key (pid, uid, slotNum),
foreign key (pid, uid, iid) references ItemsByPageAndUser(pid, uid, iid),
unique (pid, uid, iid)
)
Примечание 1: iidInSlot
имеет значение NULL, поэтому мы можем иметь пустые слоты, если хотим. Но если есть элемент, он должен быть проверен против таблицы элементов.
Примечание 2. Нам нужен последний FK, чтобы мы не добавляли элементы, которые не входят в набор возможных элементов для этого (пользователь, страница).
Примечание 3. Уникальное ограничение на (pid, uid, iid)
обеспечивает нашу цель разработки уникальных элементов в списке (предположение 6). Без этого мы могли бы добавить столько элементов из набора, которое идентифицируется (страница, пользователь), как нам нравится, если они находятся в разных слотах.
Теперь мы прекрасно отделяем элементы от своих слотов, сохраняя их общую зависимость от (страницы, пользователя).
Эта конструкция, безусловно, находится в 3NF и может быть в BCNF, хотя я беспокоюсь о SlotsByPageAndUser
в этом отношении.
Проблема состоит в том, что из-за единственного ограничения в таблице SlotsByPageAndUser
мощность отношения между SlotsByPageAndUser
и ItemsByPageAndUser
взаимно однозначна. В общем случае отношения 1-1, которые не являются подтипами сущностей, являются неправильными. Конечно, есть исключения, и, возможно, это одно. Но, может быть, есть еще лучший способ.,.
План B
-
Убейте таблицу
SlotsByPageAndUser
. -
Добавьте столбец
slotNum
вItemsByPageAndUser
. -
Добавьте уникальное ограничение на
(pid, uid, iid)
доItemsByPageAndUser
.
Теперь это:
create table ItemsByPageAndUser (
pid int not null references pages(pid),
uid int not null references users(uid),
iid int not null references items(iid),
slotNum int,
primary key (pid, uid, iid),
unique (pid, uid, slotNum)
)
Примечание 4. Оставляя slotNum
значение nullable, сохраняет нашу способность указывать элементы в наборе, которых нет в списке. Но.,.
Примечание 5. Помещение уникального ограничения на выражение, включающее столбец с нулевым значением, может привести к "интересным" результатам в некоторых базах данных. Я думаю, что это будет работать, как мы планируем в Postgres. (См. это обсуждение здесь, на SO.) Для других баз данных ваш пробег может отличаться.
Теперь нет беспорядочных отношений 1-1, которые вокруг, так что лучше.
Это все еще 3NF, поскольку единственный неключевой атрибут (slotNum
) зависит от ключа, всего ключа и всего лишь от ключа. (Вы не можете спросить о slotNum
, не сообщая мне, какую страницу, пользователя и элемент вы говорите.)
Это не BCNF, потому что [ (pid, uid, iid)
→ slotNum
] и [(pid,uid,slotNum)
→ iid
]. Но поэтому у нас есть уникальное ограничение на (pid, uid, slotNum), которое предотвращает попадание данных в несогласованное состояние.
Я думаю, что это приемлемое решение.