#mysql #hierarchical-data #adjacency-list
Вопрос:
Иерархические данные в MySQL с использованием модели списка смежности
У меня есть эта таблица с именем node_structur_data
CREATE TABLE node_structure_data (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(455) NOT NULL,
parent_id INT(10) UNSIGNED DEFAULT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parent_id) REFERENCES node_structure_data (id)
ON DELETE CASCADE ON UPDATE CASCADE
);
Выход:
id title parent_id
1 Division NULL
2 Site 1 1
3 Paper 2
4 ms1 3
Как я могу дублировать узел и его дочерние элементы?
Например Site 1
Идентификатор и родительский идентификатор должны быть уникальными, но название должно оставаться прежним.
Ожидаемый Результат:
id title parent_id
1 Division NULL
2 Site 1 1
3 Paper 2
4 ms1 3
5 Site 1 1
6 Paper 5
7 ms1 6
Комментарии:
1. Какая версия MySQL? Можете ли вы добавить к своему вопросу, каким будет содержимое таблицы, когда алгоритм будет выполнен для
Site 1
примера?2. Какая версия MySQL?
3. Версия 8.0 @trincot
4. что такое ms1 ? его даже нет внутри вывода . и вы хотите, чтобы это было внутри желаемого результата ?
5. Для этого вам, вероятно, придется написать хранимую процедуру.
Ответ №1:
Следующий подход сначала оценивает новый максимум, а затем использует рекурсивный cte для поиска всех дочерних элементов нужного узла «Сайт 1» и определения их новых возможных parent_id
, если в таблице не было других одновременных записей.
Я бы рекомендовал выполнить следующее в транзакции и заблокировать таблицу во время операции, чтобы предотвратить одновременные изменения таблицы.
Чтобы проверить этот подход, я добавил некоторые дополнительные примеры данных, которые я включил ниже, однако вы можете увидеть этот подход в демонстрационном примере с вашими исходными образцами данных здесь
Смотрите вывод рабочей скрипки бд ниже:
Схема (MySQL v8.0)
CREATE TABLE node_structure_data (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(455) NOT NULL,
parent_id INT(10) UNSIGNED DEFAULT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parent_id) REFERENCES node_structure_data (id)
ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO node_structure_data
(`id`, `title`, `parent_id`)
VALUES
('1', 'Division', NULL),
('2', 'Site 1', '1'),
('3', 'Paper', '2'),
('4', 'ms1', '3'),
('5', 'ms2', '3'),
('6', 'os1', '4'),
('7', 'os2', '4'),
('8', 'gs1', '1'),
('9', 'hs1', '3'),
('10','js1','9');
Запрос № 1
select 'Before Insert';
Перед Вставкой |
---|
Before Insert |
Query #2
select * from node_structure_data;
id | title | parent_id |
---|---|---|
1 | Division | |
2 | Сайт 1 | 1 |
3 | Бумага | 2 |
4 | мс1 | 3 |
5 | мс2 | 3 |
6 | ос1 | 4 |
7 | ос2 | 4 |
8 | gs1 | 1 |
9 | hs1 | 3 |
10 | сп1 | 9 |
Запрос № 3
select 'Possible Data Changes';
Возможные Изменения Данных |
---|
Возможные Изменения Данных |
Запрос № 4
with recursive max_id AS (
SELECT MAX(id) as id FROM node_structure_data
),
child_nodes AS (
SELECT
n.id,
title,
parent_id,
m.id 1 as new_id,
parent_id as new_parent_id
FROM
node_structure_data n
CROSS JOIN
max_id as m
WHERE
title='Site 1'
UNION ALL
SELECT
n.id,
n.title,
n.parent_id,
@row_num:=IF(@row_num=0,c.new_id,0) 1 @row_num as new_id,
c.new_id
FROM
child_nodes c
INNER JOIN
node_structure_data n ON n.parent_id = c.id
CROSS JOIN (
SELECT @row_num:=0 as rn
) as vars
)
SELECT * FROM child_nodes;
ID | Название | parent_id | новый идентификатор | new_parent_id |
---|---|---|---|---|
2 | Сайт 1 | 1 | 11 | 1 |
3 | Бумага | 2 | 12 | 11 |
4 | мс1 | 3 | 13 | 12 |
5 | мс2 | 3 | 14 | 12 |
9 | hs1 | 3 | 15 | 12 |
6 | ос1 | 4 | 16 | 13 |
7 | ос2 | 4 | 17 | 13 |
10 | сп1 | 9 | 18 | 15 |
Запрос № 5 — Выполнение фактической вставки
INSERT INTO node_structure_data (title,parent_id)
with recursive max_id AS (
SELECT MAX(id) as id FROM node_structure_data
),
child_nodes AS (
SELECT
n.id,
title,
parent_id,
m.id 1 as new_id,
parent_id as new_parent_id
FROM
node_structure_data n
CROSS JOIN
max_id as m
WHERE
title='Site 1'
UNION ALL
SELECT
n.id,
n.title,
n.parent_id,
@row_num:=IF(@row_num=0,c.new_id,0) 1 @row_num as new_id,
c.new_id
FROM
child_nodes c
INNER JOIN
node_structure_data n ON n.parent_id = c.id
CROSS JOIN (
SELECT @row_num:=0 as rn
) as vars
)
SELECT title,new_parent_id FROM child_nodes ORDER BY new_id;
Результатов для отображения нет.
Query #6
select 'AFTER INSERT';
ПОСЛЕ ВСТАВКИ |
---|
ПОСЛЕ ВСТАВКИ |
Запрос № 7
select * from node_structure_data;
ID | Название | parent_id |
---|---|---|
1 | Деление | |
2 | Сайт 1 | 1 |
3 | Бумага | 2 |
4 | мс1 | 3 |
5 | мс2 | 3 |
6 | ос1 | 4 |
7 | ос2 | 4 |
8 | gs1 | 1 |
9 | hs1 | 3 |
10 | сп1 | 9 |
11 | Сайт 1 | 1 |
12 | Бумага | 11 |
13 | мс1 | 12 |
14 | мс2 | 12 |
15 | hs1 | 12 |
16 | ос1 | 13 |
17 | ос2 | 13 |
18 | сп1 | 15 |
Дайте мне знать, если это сработает для вас.
Комментарии:
1. Отличный ответ! Это сработало!