Как организовать хранение многие ко многим отношениям в Json в MySQL

0

У меня есть таблица с полем JSON (пример)

# table1

id | json_column
---+------------------------
1  | {'table2_ids':[1,2,3], 'sone_other_data':'foo'}
---+------------------------
2  | {'foo_data':'bar', 'table2_ids':[3,5,11]}

А также

# table2

id | title
---+------------------------
1  | title1
---+------------------------
2  | title2
---+------------------------
...
---+------------------------
11 | title11

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

Я знаю о сгенерированных столбцах в MySQL, но я не понимаю, как использовать его для хранения m2m-отношений. Возможно, у меня есть использование views чтобы получить пары table1.id <-> table2.id. Но как использовать индекс в этом случае?

Теги:
many-to-many

1 ответ

0

Я не могу понять ваше объяснение, почему вы не можете использовать третью таблицу для представления пары "многие-ко-многим". Разумеется, использование третьей таблицы - лучшее решение.

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

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

create table table1 (
  id int auto_increment primary key,
  json_column json,
  first_table2_id int as (json_extract(json_column, '$.table2_ids[0]'))
);

insert into table1 set json_column = '{"table2_ids":[1,2,3], "sone_other_data":"foo"}'

(Вы должны использовать двойные кавычки внутри строки JSON и одиночные кавычки, чтобы разграничить всю строку JSON.)

select * from table1;
+----+-----------------------------------------------------+-----------------+
| id | json_column                                         | first_table2_id |
+----+-----------------------------------------------------+-----------------+
|  1 | {"table2_ids": [1, 2, 3], "sone_other_data": "foo"} |               1 |
+----+-----------------------------------------------------+-----------------+

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

Поэтому вам нужно создать еще один дополнительный столбец для каждого потенциального члена массива table2_ids. Если массив имеет меньше элементов, чем количество столбцов, JSON_EXTRACT() заполняет NULL, когда выражение ничего не возвращает.

alter table table1 add column second_table2_id int as (json_extract(json_column, '$.table2_ids[1]'));
alter table table1 add column third_table2_id int as (json_extract(json_column, '$.table2_ids[2]'));
alter table table1 add column fourth_table2_id int as (json_extract(json_column, '$.table2_ids[3]'));

Я запрошу использование вертикального вывода, поэтому столбцы будут легче читать:

select * from table1\G
*************************** 1. row ***************************
              id: 1
     json_column: {"table2_ids": [1, 2, 3], "sone_other_data": "foo"}
 first_table2_id: 1
second_table2_id: 2
 third_table2_id: 3
fourth_table2_id: NULL

Это будет очень неудобно. Сколько столбцов вам нужно? Это зависит от того, сколько table2_ids является максимальной длиной массива.

Если вам нужно искать строки в таблице 1, которые ссылаются на определенный идентификатор таблицы2, какой столбец следует искать? Любой из столбцов может иметь это значение.

select * from table1
where first_table2_id = 2
or second_table2_id = 2
or third_table2_id = 2
or fourth_table2_id = 2;

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

Это некоторые причины, по которым хранение разделенных запятыми списков - плохая идея, даже внутри строки JSON, если вам нужно ссылаться на отдельные элементы.

Лучшее решение - использовать традиционную третью таблицу для хранения данных "многие ко многим". Каждое значение сохраняется в собственной строке, поэтому вам не нужно много столбцов или много индексов. Вы можете искать один столбец, если вам нужно искать ссылки на заданное значение.

select * from table1_table2 where table2_id = 2;
  • 0
    I can't understand your explanation for why you can't use a third table to represent the many-to-many pairs. Using a third table is of course the best solution. Я не хочу использовать таблицу отношений, потому что данные об отношениях уже существуют в json_column. Но это кажется невозможным. Спасибо за ответ
  • 0
    По этой причине вам также не следует хранить массив «table2_ids» в json_column , потому что значения уже существуют как первичные ключи в table2.id . Хранение значений внешнего ключа является нормальной и обязательной частью реляционной базы данных.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню