Algorithm SQL中邻接表到前序树遍历的递归查询?

Algorithm SQL中邻接表到前序树遍历的递归查询?,algorithm,data-structures,rdbms,nested-sets,rdbms-agnostic,Algorithm,Data Structures,Rdbms,Nested Sets,Rdbms Agnostic,我正在将数据从一个数据库模式迁移到另一个数据库模式。旧的 schema有一个基于邻接列表的分类系统,具有 id、category和parent_id。如果一个category在另一个之下,则 类别将第二个id作为其父id。例如: +-------------+----------------------+--------+ | category_id | name | parent | +-------------+----------------------+-

我正在将数据从一个数据库模式迁移到另一个数据库模式。旧的 schema有一个基于邻接列表的分类系统,具有 id、category和parent_id。如果一个category在另一个之下,则 类别将第二个id作为其父id。例如:

+-------------+----------------------+--------+
| category_id | name                 | parent |
+-------------+----------------------+--------+
|           1 | ELECTRONICS          |   NULL |
|           2 | TELEVISIONS          |      1 |
|           3 | TUBE                 |      2 |
|           4 | LCD                  |      2 |
|           5 | PLASMA               |      2 |
|           6 | PORTABLE ELECTRONICS |      1 |
|           7 | MP3 PLAYERS          |      6 |
|           8 | FLASH                |      7 |
|           9 | CD PLAYERS           |      6 |
|          10 | 2 WAY RADIOS         |      6 |
+-------------+----------------------+--------+
新模式有一个修改的前序树遍历算法:

+-------------+----------------------+-----+-----+
| category_id | name                 | lft | rgt |
+-------------+----------------------+-----+-----+
|           1 | ELECTRONICS          |   1 |  20 |
|           2 | TELEVISIONS          |   2 |   9 |
|           3 | TUBE                 |   3 |   4 |
|           4 | LCD                  |   5 |   6 |
|           5 | PLASMA               |   7 |   8 |
|           6 | PORTABLE ELECTRONICS |  10 |  19 |
|           7 | MP3 PLAYERS          |  11 |  14 |
|           8 | FLASH                |  12 |  13 |
|           9 | CD PLAYERS           |  15 |  16 |
|          10 | 2 WAY RADIOS         |  17 |  18 |
+-------------+----------------------+-----+-----+
文章中的例子

无论如何,我有能力或编写一个带有递归函数的php脚本 这将把邻接列表迁移到预排序树结构。 基本上,对于每一行,它都会插入一个空白的“rgt”值,看起来 对于儿童,将函数递归地应用于他们,保持跟踪 ,然后更新“rgt”值

但我想在纯SQL中实现这一点。然而,我知道的还不够多 它的立足点。首先,我不知道你是否可以用
递归查询,或者如果有其他方法来实现它

这是我得到的;这是一个例子,可以根据您的情况进行概括或调整

首先,我将建立一个邻接列表(来自维基百科的语系数据):

下面是一个要演示的查询:

mysql> SELECT t1.language AS lev1, t2.language as lev2, t3.language as lev3, t4.language as lev4, t5.language AS lev5
    -> FROM language_family_adj_list AS t1
    -> LEFT JOIN language_family_adj_list AS t2 ON t2.parent_id = t1.language_id
    -> LEFT JOIN language_family_adj_list AS t3 ON t3.parent_id = t2.language_id
    -> LEFT JOIN language_family_adj_list AS t4 ON t4.parent_id = t3.language_id
    -> LEFT JOIN language_family_adj_list AS t5 ON t5.parent_id = t4.language_id
    -> WHERE t1.parent_id IS NULL
    -> ORDER BY t1.language, t2.language, t3.language, t4.language, t5.language;
+-------------+---------------+--------------+------------------+------+
| lev1        | lev2          | lev3         | lev4             | lev5 |
+-------------+---------------+--------------+------------------+------+
| Finno-Ugric | Baltic-Finnic | Estonian     | South Estonian   | Voro | 
| Finno-Ugric | Baltic-Finnic | Finnish      | NULL             | NULL | 
| Finno-Ugric | Baltic-Finnic | Ingrian      | NULL             | NULL | 
| Finno-Ugric | Baltic-Finnic | Karelian     | Karelian Proper  | NULL | 
| Finno-Ugric | Baltic-Finnic | Karelian     | Lude             | NULL | 
| Finno-Ugric | Baltic-Finnic | Karelian     | Olonets Karelian | NULL | 
| Finno-Ugric | Baltic-Finnic | Livonian     | NULL             | NULL | 
| Finno-Ugric | Baltic-Finnic | Veps         | NULL             | NULL | 
| Finno-Ugric | Baltic-Finnic | Votic        | NULL             | NULL | 
| Finno-Ugric | Hungarian     | NULL         | NULL             | NULL | 
| Finno-Ugric | Khanty        | NULL         | NULL             | NULL | 
| Finno-Ugric | Mansi         | NULL         | NULL             | NULL | 
| Finno-Ugric | Mari          | NULL         | NULL             | NULL | 
| Finno-Ugric | Mordvinic     | Erzya        | NULL             | NULL | 
| Finno-Ugric | Mordvinic     | Moksha       | NULL             | NULL | 
| Finno-Ugric | Permic        | Komi         | NULL             | NULL | 
| Finno-Ugric | Permic        | Komi-Permyak | NULL             | NULL | 
| Finno-Ugric | Permic        | Udmurt       | NULL             | NULL | 
| Finno-Ugric | Sami          | Eastern Sami | Akkala Sami      | NULL | 
| Finno-Ugric | Sami          | Eastern Sami | Inari Sami       | NULL | 
| Finno-Ugric | Sami          | Eastern Sami | Kemi Sami        | NULL | 
| Finno-Ugric | Sami          | Eastern Sami | Kildin Sami      | NULL | 
| Finno-Ugric | Sami          | Eastern Sami | Skolt Sami       | NULL | 
| Finno-Ugric | Sami          | Eastern Sami | Ter Sami         | NULL | 
| Finno-Ugric | Sami          | Western Sami | Lule Sami        | NULL | 
| Finno-Ugric | Sami          | Western Sami | Northern Sami    | NULL | 
| Finno-Ugric | Sami          | Western Sami | Pite Sami        | NULL | 
| Finno-Ugric | Sami          | Western Sami | Southern Sami    | NULL | 
| Finno-Ugric | Sami          | Western Sami | Umi Sami         | NULL | 
+-------------+---------------+--------------+------------------+------+
29 rows in set (0.00 sec)
下面是修改后的前序遍历树表模式:

CREATE TABLE language_family_mptt (
 language VARCHAR(30) NOT NULL,
 lft INT NOT NULL,
 rgt INT NOT NULL
) COLLATE utf8;
下面是迁移数据的递归存储过程:

TRUNCATE TABLE language_family_mptt;
SET max_sp_recursion_depth = 255;
DROP PROCEDURE IF EXISTS insert_branches;
DROP PROCEDURE IF EXISTS start_tree;
DELIMITER ~~

CREATE PROCEDURE start_tree()
BEGIN
    DECLARE language_field VARCHAR(100);
    DECLARE done INT DEFAULT 0;
    DECLARE insert_id INT;
    DECLARE source_id INT;

    DECLARE cursor1 CURSOR FOR SELECT language, language_id FROM language_family_adj_list WHERE parent_id IS NULL ORDER BY language;

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN cursor1;
    read_loop: LOOP

        SET @my_left = 1;

        FETCH cursor1 INTO language_field, source_id;
        INSERT INTO language_family_mptt ( language, lft ) VALUES ( language_field, 1 );

        CALL insert_branches( source_id );

        UPDATE language_family_mptt SET rgt = @my_left + 1 WHERE lft = 1 AND rgt = 0;

        IF done THEN
            LEAVE read_loop;
        END IF;

    END LOOP;
    CLOSE cursor1;

END; ~~

CREATE PROCEDURE insert_branches( IN source_parent_id INT )
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE next_source_parent_id INT DEFAULT NULL;
    DECLARE orig_left INT DEFAULT NULL;
    DECLARE language_field VARCHAR(100);    
    DECLARE cursor1 CURSOR FOR SELECT language_id, language FROM language_family_adj_list WHERE parent_id = source_parent_id ORDER BY language;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN cursor1;

    read_loop: LOOP

        FETCH cursor1 INTO next_source_parent_id, language_field;

        IF done THEN
            LEAVE read_loop;
        END IF;

        SET @my_left = @my_left + 1;

        INSERT INTO language_family_mptt ( language, lft ) VALUES ( language_field, @my_left ); 

        SET orig_left = @my_left;

        CALL insert_branches( next_source_parent_id );

        UPDATE language_family_mptt SET rgt = @my_left + 1 WHERE lft = orig_left AND rgt = 0 ;

        SET @my_left = @my_left + 1;

    END LOOP;
    CLOSE cursor1;
END; ~~

DELIMITER ;
结果如下:

mysql> SELECT CONCAT( REPEAT( ' ', (COUNT(parent.language) - 1) ), node.language) AS name FROM language_family_mptt AS node, language_family_mptt AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.language ORDER BY node.lft;
+---------------------------------+
| name                            |
+---------------------------------+
|    Finno-Ugric                  | 
|        Baltic-Finnic            | 
|            Estonian             | 
|                South Estonian   | 
|                    Voro         | 
|            Finnish              | 
|            Ingrian              | 
|            Karelian             | 
|                Karelian Proper  | 
|                Lude             | 
|                Olonets Karelian | 
|            Livonian             | 
|            Veps                 | 
|            Votic                | 
|        Hungarian                | 
|        Khanty                   | 
|        Mansi                    | 
|        Mari                     | 
|        Mordvinic                | 
|            Erzya                | 
|            Moksha               | 
|        Permic                   | 
|            Komi                 | 
|            Komi-Permyak         | 
|            Udmurt               | 
|        Sami                     | 
|            Eastern Sami         | 
|                Akkala Sami      | 
|                Inari Sami       | 
|                Kemi Sami        | 
|                Kildin Sami      | 
|                Skolt Sami       | 
|                Ter Sami         | 
|            Western Sami         | 
|                Lule Sami        | 
|                Northern Sami    | 
|                Pite Sami        | 
|                Southern Sami    | 
|                Umi Sami         | 
+---------------------------------+
39 rows in set (0.00 sec)

:D

您是否有已知数量的层次结构级别-这是一个静态数据集,还是需要处理“任何”数据集?在我处理的数据集中,它或多或少是静态的,因此存在已知级别。但我确实在寻找一个通用算法。递归存储过程…非常好。我尝试过这个,但“rgt”列未填充,lft值正常,但所有行的rgt都为空。我只是在mysql 5.1.63-0+debian上通过复制/粘贴尝试了这一点,并且工作正常。原始邻接列表是否正确填充?
mysql> SELECT CONCAT( REPEAT( ' ', (COUNT(parent.language) - 1) ), node.language) AS name FROM language_family_mptt AS node, language_family_mptt AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.language ORDER BY node.lft;
+---------------------------------+
| name                            |
+---------------------------------+
|    Finno-Ugric                  | 
|        Baltic-Finnic            | 
|            Estonian             | 
|                South Estonian   | 
|                    Voro         | 
|            Finnish              | 
|            Ingrian              | 
|            Karelian             | 
|                Karelian Proper  | 
|                Lude             | 
|                Olonets Karelian | 
|            Livonian             | 
|            Veps                 | 
|            Votic                | 
|        Hungarian                | 
|        Khanty                   | 
|        Mansi                    | 
|        Mari                     | 
|        Mordvinic                | 
|            Erzya                | 
|            Moksha               | 
|        Permic                   | 
|            Komi                 | 
|            Komi-Permyak         | 
|            Udmurt               | 
|        Sami                     | 
|            Eastern Sami         | 
|                Akkala Sami      | 
|                Inari Sami       | 
|                Kemi Sami        | 
|                Kildin Sami      | 
|                Skolt Sami       | 
|                Ter Sami         | 
|            Western Sami         | 
|                Lule Sami        | 
|                Northern Sami    | 
|                Pite Sami        | 
|                Southern Sami    | 
|                Umi Sami         | 
+---------------------------------+
39 rows in set (0.00 sec)