Модель списка смежности дублирует родителя и детей

#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. Отличный ответ! Это сработало!