ค้นหาระดับสูงสุดของฟิลด์ลำดับชั้น: ด้วย vs ไม่มี CTEs


58

หมายเหตุ: คำถามนี้ได้รับการปรับปรุงเพื่อสะท้อนให้เห็นว่าขณะนี้เรากำลังใช้ MySQL โดยทำเช่นนั้นฉันอยากจะดูว่ามันจะง่ายกว่านี้ถ้าเราเปลี่ยนไปใช้ฐานข้อมูลที่สนับสนุน CTE

ผมมีตารางอ้างอิงตนเองกับคีย์หลักและต่างประเทศที่สำคัญidparent_id

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment | 
| parent_id  | int(11)      | YES  |     | NULL    |                | 
| name       | varchar(255) | YES  |     | NULL    |                | 
| notes      | text         | YES  |     | NULL    |                | 
+------------+--------------+------+-----+---------+----------------+

ได้รับ nameไว้ฉันจะสอบถามผู้ปกครองระดับบนสุดได้อย่างไร

ที่ได้รับnameฉันจะสอบถามทั้งหมดที่idเกี่ยวข้องกับบันทึกได้name = 'foo'อย่างไร

บริบท:ฉันไม่ใช่ dba แต่กำลังวางแผนที่จะขอให้ dba ใช้โครงสร้างลำดับชั้นชนิดนี้และต้องการทดสอบบางคำถาม แรงจูงใจในการทำเช่นนั้นจะถูกอธิบายโดยKattge et al, 2011


นี่คือตัวอย่างของความสัมพันธ์ระหว่างรหัสในตาราง:

ป้อนคำอธิบายรูปภาพที่นี่

-- -----------------------------------------------------
-- Create a new database called 'testdb'
-- -----------------------------------------------------
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

CREATE SCHEMA IF NOT EXISTS `testdb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `testdb` ;

-- -----------------------------------------------------
-- Table `testdb`.`observations`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `testdb`.`observations` (
  `id` INT NOT NULL ,
  `parent_id` INT NULL ,
  `name` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

-- -----------------------------------------------------
-- Add Example Data Set
-- -----------------------------------------------------


INSERT INTO observations VALUES (1,3), (2,5), (3,NULL), (4,10), 
   (5,NULL), (6,1), (7,5), (8,10), (9,10), (10,3);

4
MySQL เป็นข้อยกเว้นของคำสั่ง "เกือบทั้งหมด DBMS" ไม่รองรับการสอบถามซ้ำ โอกาสเดียวที่คุณมีคือการเขียนโพรซีเดอร์ที่เก็บซึ่งวนซ้ำผ่านทรีและดึงข้อมูล หรือ - หากจำนวนของระดับถูก จำกัด - ใช้จำนวนการรวมตัวเองที่น่า
กลัว

คำตอบ:


63

คุณต้องเขียนสคริปต์นี้ผ่านทาง MySQL Stored Procedure Language

นี่คือฟังก์ชั่นที่จัดเก็บเรียกว่าGetParentIDByIDดึง ParentID ที่ได้รับ ID เพื่อค้นหา

DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetParentIDByID` $$
CREATE FUNCTION `junk`.`GetParentIDByID` (GivenID INT) RETURNS INT
DETERMINISTIC
BEGIN
    DECLARE rv INT;

    SELECT IFNULL(parent_id,-1) INTO rv FROM
    (SELECT parent_id FROM pctable WHERE id = GivenID) A;
    RETURN rv;
END $$
DELIMITER ;

นี่คือฟังก์ชั่นที่จัดเก็บเรียกว่าGetAncestryเพื่อดึงรายการของ ParentIDs เริ่มต้นจากการสร้างครั้งแรกทุกลำดับชั้นขึ้นให้ ID เริ่มต้นด้วย:

DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetAncestry` $$
CREATE FUNCTION `junk`.`GetAncestry` (GivenID INT) RETURNS VARCHAR(1024)
DETERMINISTIC
BEGIN
    DECLARE rv VARCHAR(1024);
    DECLARE cm CHAR(1);
    DECLARE ch INT;

    SET rv = '';
    SET cm = '';
    SET ch = GivenID;
    WHILE ch > 0 DO
        SELECT IFNULL(parent_id,-1) INTO ch FROM
        (SELECT parent_id FROM pctable WHERE id = ch) A;
        IF ch > 0 THEN
            SET rv = CONCAT(rv,cm,ch);
            SET cm = ',';
        END IF;
    END WHILE;
    RETURN rv;
END $$
DELIMITER ;

นี่คือสิ่งที่จะสร้างข้อมูลตัวอย่าง:

USE junk
DROP TABLE IF EXISTS pctable;
CREATE TABLE pctable
(
    id INT NOT NULL AUTO_INCREMENT,
    parent_id INT,
    PRIMARY KEY (id)
) ENGINE=MyISAM;
INSERT INTO pctable (parent_id) VALUES (0);
INSERT INTO pctable (parent_id) SELECT parent_id+1 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+2 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+3 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+4 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+5 FROM pctable;
SELECT * FROM pctable;

นี่คือสิ่งที่มันสร้าง:

mysql> USE junk
Database changed
mysql> DROP TABLE IF EXISTS pctable;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE pctable
    -> (
    ->     id INT NOT NULL AUTO_INCREMENT,
    ->     parent_id INT,
    ->     PRIMARY KEY (id)
    -> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.05 sec)

mysql> INSERT INTO pctable (parent_id) VALUES (0);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO pctable (parent_id) SELECT parent_id+1 FROM pctable;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pctable (parent_id) SELECT parent_id+2 FROM pctable;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pctable (parent_id) SELECT parent_id+3 FROM pctable;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pctable (parent_id) SELECT parent_id+4 FROM pctable;
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> INSERT INTO pctable (parent_id) SELECT parent_id+5 FROM pctable;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM pctable;
+----+-----------+
| id | parent_id |
+----+-----------+
|  1 |         0 |
|  2 |         1 |
|  3 |         2 |
|  4 |         3 |
|  5 |         3 |
|  6 |         4 |
|  7 |         5 |
|  8 |         6 |
|  9 |         4 |
| 10 |         5 |
| 11 |         6 |
| 12 |         7 |
| 13 |         7 |
| 14 |         8 |
| 15 |         9 |
| 16 |        10 |
| 17 |         5 |
| 18 |         6 |
| 19 |         7 |
| 20 |         8 |
| 21 |         8 |
| 22 |         9 |
| 23 |        10 |
| 24 |        11 |
| 25 |         9 |
| 26 |        10 |
| 27 |        11 |
| 28 |        12 |
| 29 |        12 |
| 30 |        13 |
| 31 |        14 |
| 32 |        15 |
+----+-----------+
32 rows in set (0.00 sec)

นี่คือสิ่งที่ฟังก์ชั่นสร้างขึ้นสำหรับแต่ละค่า:

mysql> SELECT id,GetParentIDByID(id),GetAncestry(id) FROM pctable;
+----+---------------------+-----------------+
| id | GetParentIDByID(id) | GetAncestry(id) |
+----+---------------------+-----------------+
|  1 |                   0 |                 |
|  2 |                   1 | 1               |
|  3 |                   2 | 2,1             |
|  4 |                   3 | 3,2,1           |
|  5 |                   3 | 3,2,1           |
|  6 |                   4 | 4,3,2,1         |
|  7 |                   5 | 5,3,2,1         |
|  8 |                   6 | 6,4,3,2,1       |
|  9 |                   4 | 4,3,2,1         |
| 10 |                   5 | 5,3,2,1         |
| 11 |                   6 | 6,4,3,2,1       |
| 12 |                   7 | 7,5,3,2,1       |
| 13 |                   7 | 7,5,3,2,1       |
| 14 |                   8 | 8,6,4,3,2,1     |
| 15 |                   9 | 9,4,3,2,1       |
| 16 |                  10 | 10,5,3,2,1      |
| 17 |                   5 | 5,3,2,1         |
| 18 |                   6 | 6,4,3,2,1       |
| 19 |                   7 | 7,5,3,2,1       |
| 20 |                   8 | 8,6,4,3,2,1     |
| 21 |                   8 | 8,6,4,3,2,1     |
| 22 |                   9 | 9,4,3,2,1       |
| 23 |                  10 | 10,5,3,2,1      |
| 24 |                  11 | 11,6,4,3,2,1    |
| 25 |                   9 | 9,4,3,2,1       |
| 26 |                  10 | 10,5,3,2,1      |
| 27 |                  11 | 11,6,4,3,2,1    |
| 28 |                  12 | 12,7,5,3,2,1    |
| 29 |                  12 | 12,7,5,3,2,1    |
| 30 |                  13 | 13,7,5,3,2,1    |
| 31 |                  14 | 14,8,6,4,3,2,1  |
| 32 |                  15 | 15,9,4,3,2,1    |
+----+---------------------+-----------------+
32 rows in set (0.02 sec)

MORAL OF THE STORY: การดึงข้อมูลแบบเรียกซ้ำต้องเป็นสคริปต์ใน MySQL

อัพเดท 2011-10-24 17:17 EDT

นี่คือสิ่งที่ตรงกันข้ามของ GetAncestry ฉันเรียกมันว่า GetFamilyTree

นี่คืออัลกอริทึม:

  • สถานที่ที่ได้รับ ID ในคิว
  • ห่วง
    • โพสต์ลงใน front_id
    • ดึงรหัสทั้งหมดลงใน queue_children ที่ parent_id = front_id
    • ผนวก queue_children เข้ากับ retval_list (rv)
    • เข้าคิว Que_children
    • ทำซ้ำจนกว่าคิวและ queue_children จะว่างเปล่าพร้อมกัน

ฉันเชื่อว่าจากโครงสร้างข้อมูลและคลาสอัลกอริทึมของฉันในวิทยาลัยสิ่งนี้เรียกว่า preorder / prefix tree traversal

นี่คือรหัส:

DELIMITER $$

DROP FUNCTION IF EXISTS `junk`.`GetFamilyTree` $$
CREATE FUNCTION `junk`.`GetFamilyTree` (GivenID INT) RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN

    DECLARE rv,q,queue,queue_children VARCHAR(1024);
    DECLARE queue_length,front_id,pos INT;

    SET rv = '';
    SET queue = GivenID;
    SET queue_length = 1;

    WHILE queue_length > 0 DO
        SET front_id = FORMAT(queue,0);
        IF queue_length = 1 THEN
            SET queue = '';
        ELSE
            SET pos = LOCATE(',',queue) + 1;
            SET q = SUBSTR(queue,pos);
            SET queue = q;
        END IF;
        SET queue_length = queue_length - 1;

        SELECT IFNULL(qc,'') INTO queue_children
        FROM (SELECT GROUP_CONCAT(id) qc
        FROM pctable WHERE parent_id = front_id) A;

        IF LENGTH(queue_children) = 0 THEN
            IF LENGTH(queue) = 0 THEN
                SET queue_length = 0;
            END IF;
        ELSE
            IF LENGTH(rv) = 0 THEN
                SET rv = queue_children;
            ELSE
                SET rv = CONCAT(rv,',',queue_children);
            END IF;
            IF LENGTH(queue) = 0 THEN
                SET queue = queue_children;
            ELSE
                SET queue = CONCAT(queue,',',queue_children);
            END IF;
            SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
        END IF;
    END WHILE;

    RETURN rv;

END $$

นี่คือสิ่งที่แต่ละแถวผลิต

mysql> SELECT id,parent_id,GetParentIDByID(id),GetAncestry(id),GetFamilyTree(id) FROM pctable;
+----+-----------+---------------------+-----------------+--------------------------------------------------------------------------------------+
| id | parent_id | GetParentIDByID(id) | GetAncestry(id) | GetFamilyTree(id)                                                                    |
+----+-----------+---------------------+-----------------+--------------------------------------------------------------------------------------+
|  1 |         0 |                   0 |                 | 2,3,4,5,6,9,7,10,17,8,11,18,15,22,25,12,13,19,16,23,26,14,20,21,24,27,32,28,29,30,31 |
|  2 |         1 |                   1 | 1               | 3,4,5,6,9,7,10,17,8,11,18,15,22,25,12,13,19,16,23,26,14,20,21,24,27,32,28,29,30,31   |
|  3 |         2 |                   2 | 2,1             | 4,5,6,9,7,10,17,8,11,18,15,22,25,12,13,19,16,23,26,14,20,21,24,27,32,28,29,30,31     |
|  4 |         3 |                   3 | 3,2,1           | 6,9,8,11,18,15,22,25,14,20,21,24,27,32,31                                            |
|  5 |         3 |                   3 | 3,2,1           | 7,10,17,12,13,19,16,23,26,28,29,30                                                   |
|  6 |         4 |                   4 | 4,3,2,1         | 8,11,18,14,20,21,24,27,31                                                            |
|  7 |         5 |                   5 | 5,3,2,1         | 12,13,19,28,29,30                                                                    |
|  8 |         6 |                   6 | 6,4,3,2,1       | 14,20,21,31                                                                          |
|  9 |         4 |                   4 | 4,3,2,1         | 15,22,25,32                                                                          |
| 10 |         5 |                   5 | 5,3,2,1         | 16,23,26                                                                             |
| 11 |         6 |                   6 | 6,4,3,2,1       | 24,27                                                                                |
| 12 |         7 |                   7 | 7,5,3,2,1       | 28,29                                                                                |
| 13 |         7 |                   7 | 7,5,3,2,1       | 30                                                                                   |
| 14 |         8 |                   8 | 8,6,4,3,2,1     | 31                                                                                   |
| 15 |         9 |                   9 | 9,4,3,2,1       | 32                                                                                   |
| 16 |        10 |                  10 | 10,5,3,2,1      |                                                                                      |
| 17 |         5 |                   5 | 5,3,2,1         |                                                                                      |
| 18 |         6 |                   6 | 6,4,3,2,1       |                                                                                      |
| 19 |         7 |                   7 | 7,5,3,2,1       |                                                                                      |
| 20 |         8 |                   8 | 8,6,4,3,2,1     |                                                                                      |
| 21 |         8 |                   8 | 8,6,4,3,2,1     |                                                                                      |
| 22 |         9 |                   9 | 9,4,3,2,1       |                                                                                      |
| 23 |        10 |                  10 | 10,5,3,2,1      |                                                                                      |
| 24 |        11 |                  11 | 11,6,4,3,2,1    |                                                                                      |
| 25 |         9 |                   9 | 9,4,3,2,1       |                                                                                      |
| 26 |        10 |                  10 | 10,5,3,2,1      |                                                                                      |
| 27 |        11 |                  11 | 11,6,4,3,2,1    |                                                                                      |
| 28 |        12 |                  12 | 12,7,5,3,2,1    |                                                                                      |
| 29 |        12 |                  12 | 12,7,5,3,2,1    |                                                                                      |
| 30 |        13 |                  13 | 13,7,5,3,2,1    |                                                                                      |
| 31 |        14 |                  14 | 14,8,6,4,3,2,1  |                                                                                      |
| 32 |        15 |                  15 | 15,9,4,3,2,1    |                                                                                      |
+----+-----------+---------------------+-----------------+--------------------------------------------------------------------------------------+
32 rows in set (0.04 sec)

อัลกอริทึมนี้ทำงานได้อย่างสมบูรณ์หากไม่มีเส้นทางแบบวนรอบ หากมีเส้นทางแบบวงกลมคุณจะต้องเพิ่มคอลัมน์ 'เยี่ยมชม' ลงในตาราง

เมื่อคุณเพิ่มคอลัมน์ที่เยี่ยมชมนี่คืออัลกอริทึมที่ปิดกั้นความสัมพันธ์แบบวงกลม:

  • สถานที่ที่ได้รับ ID ในคิว
  • ทำเครื่องหมายทุกคนที่เข้าชมด้วย 0
  • ห่วง
    • โพสต์ลงใน front_id
    • ดึงรหัสทั้งหมดลงใน queue_children ที่ parent_id = front_id และเยี่ยมชม = 0
    • ทำเครื่องหมาย que_children ทั้งหมดที่ดึงมาด้วยการเยี่ยมชม = 1
    • ผนวก queue_children เข้ากับ retval_list (rv)
    • เข้าคิว Que_children
    • ทำซ้ำจนกว่าคิวและ queue_children จะว่างเปล่าพร้อมกัน

อัพเดท 2011-10-24 17:37 EDT

ฉันสร้างตารางใหม่ที่ชื่อว่าการสังเกตและเติมข้อมูลตัวอย่างของคุณ ฉันเปลี่ยนโพรซีเดอร์ที่เก็บไว้เพื่อใช้การสังเกตแทน pctable นี่คือผลลัพธ์ของคุณ:

mysql> CREATE TABLE observations LIKE pctable;
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO observations VALUES (1,3), (2,5), (3,0), (4,10),(5,0),(6,1),(7,5),(8,10),(9,10),(10,3);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM observations;
+----+-----------+
| id | parent_id |
+----+-----------+
|  1 |         3 |
|  2 |         5 |
|  3 |         0 |
|  4 |        10 |
|  5 |         0 |
|  6 |         1 |
|  7 |         5 |
|  8 |        10 |
|  9 |        10 |
| 10 |         3 |
+----+-----------+
10 rows in set (0.00 sec)

mysql> SELECT id,parent_id,GetParentIDByID(id),GetAncestry(id),GetFamilyTree(id) FROM observations;
+----+-----------+---------------------+-----------------+-------------------+
| id | parent_id | GetParentIDByID(id) | GetAncestry(id) | GetFamilyTree(id) |
+----+-----------+---------------------+-----------------+-------------------+
|  1 |         3 |                   3 |                 | 6                 |
|  2 |         5 |                   5 | 5               |                   |
|  3 |         0 |                   0 |                 | 1,10,6,4,8,9      |
|  4 |        10 |                  10 | 10,3            |                   |
|  5 |         0 |                   0 |                 | 2,7               |
|  6 |         1 |                   1 | 1               |                   |
|  7 |         5 |                   5 | 5               |                   |
|  8 |        10 |                  10 | 10,3            |                   |
|  9 |        10 |                  10 | 10,3            |                   |
| 10 |         3 |                   3 | 3               | 4,8,9             |
+----+-----------+---------------------+-----------------+-------------------+
10 rows in set (0.01 sec)

อัพเดท 2011-10-24 18:22 EDT

ฉันเปลี่ยนรหัสสำหรับ GetAncestry นอกจากนั้นWHILE ch > 1มันควรจะเป็นWHILE ch > 0

mysql> SELECT id,parent_id,GetParentIDByID(id),GetAncestry(id),GetFamilyTree(id) FROM observations;
+----+-----------+---------------------+-----------------+-------------------+
| id | parent_id | GetParentIDByID(id) | GetAncestry(id) | GetFamilyTree(id) |
+----+-----------+---------------------+-----------------+-------------------+
|  1 |         3 |                   3 | 3               | 6                 |
|  2 |         5 |                   5 | 5               |                   |
|  3 |         0 |                   0 |                 | 1,10,6,4,8,9      |
|  4 |        10 |                  10 | 10,3            |                   |
|  5 |         0 |                   0 |                 | 2,7               |
|  6 |         1 |                   1 | 1,3             |                   |
|  7 |         5 |                   5 | 5               |                   |
|  8 |        10 |                  10 | 10,3            |                   |
|  9 |        10 |                  10 | 10,3            |                   |
| 10 |         3 |                   3 | 3               | 4,8,9             |
+----+-----------+---------------------+-----------------+-------------------+
10 rows in set (0.01 sec)

ลองตอนนี้ !!!


ฟังก์ชัน GetParentIDByID (id) ทำงานได้ดีในการเลือก แต่ GetAncestry (id) ไม่ตอบสนอง SELECT id, GetParentIDByID (id) จากผู้ใช้; ใช้งานได้ดี SELECT id, GetAncestry (id) จากผู้ใช้; ไม่ตอบสนอง ... เพียงแค่แสดงการโหลดใน phpmyadmin
Simerjit Parmar

@RolandoMySQLDBA GetFamilyTree ทำงานได้ดี! แต่สำหรับต้นไม้ที่มีขนาดใหญ่ขึ้น คุณช่วยแก้ไขฟังก์ชั่น DB สำหรับส่วนที่เข้าชมได้หรือไม่
Nadeshwaran

ไม่มีใครใช้เส้นทางแบบวนซ้ำหรือไม่?
عثمانغني

บน GetFamilyTree
عثمانغني

28

ได้รับทั้งหมดพ่อแม่ของโหนดระบุ

WITH RECURSIVE tree AS ( 
   SELECT id, 
          name, 
          parent_id,
          1 as level 
   FROM the_table
   WHERE name = 'foo'

   UNION ALL 

   SELECT p.id,
          p.name,
          p.parent_id, 
          t.level + 1
   FROM the_table p
     JOIN tree t ON t.parent_id = p.id
)
SELECT *
FROM tree

ในการรับรูทโหนดคุณสามารถทำได้เช่น ORDER BY levelและรับแถวแรก

รับลูกทั้งหมดของโหนดที่ระบุ:

WITH RECURSIVE tree AS ( 
   SELECT id, 
          name, 
          parent_id,
          1 as level 
   FROM the_table
   WHERE name = 'foo'

   UNION ALL 

   SELECT p.id,
          p.name,
          p.parent_id, 
          t.level + 1
   FROM your_table p
     JOIN tree t ON t.id = p.parent_id
)
SELECT *
FROM tree

(บันทึกเงื่อนไขการสลับสำหรับการเข้าร่วมในส่วนซ้ำของคำสั่ง)

ตามความรู้ของฉัน DBMS ต่อไปนี้สนับสนุน CTE แบบเรียกซ้ำ:

  • FirebirdSQL 2.1 (จริง ๆ แล้วเป็น OpenSource DBMS แรกที่นำไปใช้)
  • PostgreSQL 8.4
  • DB2 (ไม่แน่ใจว่าเป็นเวอร์ชั่นที่แน่นอน)
  • Oracle (ตั้งแต่ 11.2)
  • SQL Server 2005 และใหม่กว่า
  • Teradata
  • H2
  • Sybase (ไม่รู้รุ่นที่แน่นอน)

แก้ไข

จากข้อมูลตัวอย่างของคุณข้อมูลต่อไปนี้จะดึงข้อมูล subtrees ทั้งหมดจากตารางรวมถึงเส้นทางที่สมบูรณ์สำหรับแต่ละโหนดเป็นคอลัมน์เพิ่มเติม:

with recursive obs_tree as (
   select id, parent_id, '/'||cast(id as varchar) as tree
   from observations
   where parent_id is null

   union all 

   select t.id, t.parent_id, p.tree||'/'||cast(t.id as varchar)
   from observations t
      join obs_tree p on t.parent_id = p.id
)
select id, parent_id, tree
from obs_tree
order by tree

ผลลัพธ์จะเป็นดังนี้:

id | parent_id | ต้นไม้
---- + ----------- + ---------
  3 | | / 3
  1 | 3 | / 3/1
  6 | 1 | / 3 / 6/1
 10 | 3 | / 3/10
  4 | 10 | / 3 / 4/10
  8 | 10 | / 3 / 8/10
  9 | 10 | / 3 / 9/10
  5 | | / 5
  2 | 5 | / 5/2
  7 | 5 | / 7/5

สิ่งนี้รับ +1 จากฉันเพราะรหัสของคุณสะอาดกว่าใช้งานง่ายและรัดกุม มีห่วงน้อยกว่ามากในการข้ามผ่าน มันเป็นความอัปยศ MySQL ไม่ได้ให้กลไกที่สง่างามมากขึ้นสำหรับการดึงข้อมูลซ้ำ
RolandoMySQLDBA

8
@RolandoMySQLDBA: จากมุมมองของฉัน MySQL ล้มเหลวอย่างสมบูรณ์เพื่อให้ทันกับคุณสมบัติ SQL ที่ทันสมัยเช่นฟังก์ชั่นหน้าต่าง, การแสดงออกของตารางซ้ำ, ฟังก์ชั่นตาราง, ดัชนีตารางตามฟังก์ชั่น, ข้อ จำกัด การตรวจสอบ, ข้อ จำกัด deferrable
a_horse_with_no_name

8

ฟังก์ชัน GetFamilyTree ในคำตอบของ Rolandoไม่ทำงานเมื่อ id ที่กำหนดมีค่ามากกว่า 4 จำนวนเต็มเนื่องจากฟังก์ชัน FORMAT MySQL เพิ่มเครื่องหมายจุลภาคสำหรับตัวคั่นหลักพัน ฉันได้ปรับเปลี่ยนฟังก์ชั่นที่เก็บไว้ GetFamilyTree ให้ทำงานกับรหัสจำนวนเต็มขนาดใหญ่ดังนี้

WHILE queue_length > 0 DO
    IF queue_length = 1 THEN
    SET front_id = queue;
        SET queue = '';
    ELSE
    SET front_id = SUBSTR(queue,1,LOCATE(',',queue)-1);
        SET pos = LOCATE(',',queue) + 1;
        SET q = SUBSTR(queue,pos);
        SET queue = q;
    END IF;

front_id ถูกกำหนดไว้ภายในถ้า else loop


การแก้ไขของคุณมีประโยชน์ แต่ฉันใช้เวลาสองสามวินาทีเพื่อค้นหาว่าคุณเปลี่ยนสายอะไร :-)
hriziya
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.