Связать строки таблицы в группы, со специальным элементом в каждой группе - PullRequest
0 голосов
/ 11 января 2020

У меня есть стол items (id, varchar name). Я хотел бы создать способ группировки item в группы. Каждый item должен быть ровно в одном group. Каждая группа должна иметь хотя бы одну item, возможно, несколько. Каждый group должен иметь ровно один из item, обозначенных как специальные, и этот item должен быть членом этого group. Должна быть возможность выбрать любой item в качестве выбранного item, а также можно изменить, какой item из group является выбранным.

Я использую последнюю версию из MySQL, таблица items использует InnoDB.

Как я могу кодировать эти ограничения в SQL дизайн? Я знаю, как создать ассоциативную таблицу n-к-1, но я не знаю, как кодировать часть «ровно один специальный элемент».

В моей текущей попытке используются таблицы group (id) и

group_item (
  id,
  group_id,
  item_id,
  unique key item_id,
  selected enum("selected") null,
  unique key (group_id, selected)
  )

но это не гарантирует, что выбран хотя бы один элемент, и я не уверен, что этот дизайн идет в правильном направлении.

К сожалению, я не могу найти хороший термин для поиска, чтобы найти литературу или код, который пытается реализовать такой дизайн - я потратил много времени на поиск в Google и в Stack Overflow, а также на несколько SQL книг.

Ответы [ 2 ]

1 голос
/ 11 января 2020

Вот возможное решение, которое, однако, не удовлетворяет всем ограничениям.

  1. Создайте таблицу групп, давайте id ее идентификатор.

  2. Добавьте в таблицу элементов внешний ключ для группы group_id. Это удовлетворяет ограничению «Каждый элемент должен быть в одной группе».

  3. Таблица групп должна иметь атрибут selected_item, внешний ключ для таблицы элементов. Это удовлетворяет ограничению «Каждая группа должна иметь ровно один из своих пунктов, обозначенных как специальные».

Ограничения, которые не удовлетворяются этим решением:

  1. В каждой группе должен быть хотя бы один элемент.

  2. В каждой группе должен быть ровно один из ее элементов, обозначенных как особые, и этот элемент должен быть членом этой группы.

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

DELETE FROM groups g 
  WHERE NOT EXISTS (SELECT * FROM items i WHERE i.group_id = g.id)

второй с запросом, который проверяет, что selected_item в каждой группе относится к некоторому элементу, у которого эта группа имеет group_id. Что-то вроде:

SELECT *
FROM groups g
WHERE selected_item NOT IN (SELECT item_id FROM items i WHERE i.group_id = g.id)

(возможно, с дополнительным условием AND selected_item IS NOT NULL, если атрибут может быть обнуляемым).

В качестве альтернативы, вы можете добавить триггеры для автоматического применения ограничений.

0 голосов
/ 12 января 2020
CREATE TABLE Items (
  item_id,
  group_id NOT NULL, -- "Each item should be in exactly one group"
  PRIMARY KEY(item_id)
  INDEX (group_id)
  )

CREATE TABLE Groups (
  group_id,
  special_item_id,  -- "the special item must be a member of that group"
  PRIMARY KEY(group_id)
  )

Нет необходимости в отдельном сопоставлении таблиц между группами элементов.

Эти два определения таблиц дают минимум, необходимый для предоставления всех необходимых ссылок. И они обрабатывают некоторые ограничений.

"изменить, какой элемент группы является выбранным" - UPDATE Groups SET special_item_id = ... WHERE ...

Что касается "Каждая группа должна иметь хотя бы один предмет "; это не применяется здесь. Для этого вам может понадобиться бизнес логи c. Или, возможно, FOREIGN KEY мог бы сделать это. Или TRIGGER. Имейте в виду, что и INSERTs, и DELETEs из каждой таблицы могут нарушить это требование.

Что касается "специальный элемент не является постоянным членом другой группы" То же самое.

...