MySQL: แบบสอบถามแบบลำดับชั้นต้นไม้


20

SUB-TREE ภายในต้นไม้ใน MySQL

ใน MYSQL ของDatabase COMPANYฉันฉันมีความTable: Employeeสัมพันธ์แบบวนซ้ำพนักงานสามารถเป็นหัวหน้าของพนักงานคนอื่นได้ A self relationship of kind (SuperVisor (1)- SuperVisee (∞) ).

แบบสอบถามเพื่อสร้างตาราง:

CREATE TABLE IF NOT EXISTS `Employee` (
  `SSN` varchar(64) NOT NULL,
  `Name` varchar(64) DEFAULT NULL,
  `Designation` varchar(128) NOT NULL,
  `MSSN` varchar(64) NOT NULL, 
  PRIMARY KEY (`SSN`),
  CONSTRAINT `FK_Manager_Employee`  
              FOREIGN KEY (`MSSN`) REFERENCES Employee(SSN)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ฉันได้แทรกชุดของ tuples (ข้อความค้นหา):

INSERT INTO Employee VALUES 
 ("1", "A", "OWNER",  "1"),  

 ("2", "B", "BOSS",   "1"), # Employees under OWNER 
 ("3", "F", "BOSS",   "1"),

 ("4", "C", "BOSS",   "2"), # Employees under B
 ("5", "H", "BOSS",   "2"), 
 ("6", "L", "WORKER", "2"), 
 ("7", "I", "BOSS",   "2"), 
 # Remaining Leaf nodes   
 ("8", "K", "WORKER", "3"), # Employee under F     

 ("9", "J", "WORKER", "7"), # Employee under I     

 ("10","G", "WORKER", "5"), # Employee under H

 ("11","D", "WORKER", "4"), # Employee under C
 ("12","E", "WORKER", "4")  

แถวที่แทรกมีการติดตามทรีแบบลำดับชั้น :

         A     <---ROOT-OWNER
        /|\             
       / A \        
      B     F 
    //| \    \          
   // |  \    K     
  / | |   \                     
 I  L H    C        
/     |   / \ 
J     G  D   E

ฉันเขียนแบบสอบถามเพื่อค้นหาความสัมพันธ์:

SELECT  SUPERVISOR.name AS SuperVisor, 
        GROUP_CONCAT(SUPERVISEE.name  ORDER BY SUPERVISEE.name ) AS SuperVisee, 
        COUNT(*)  
FROM Employee AS SUPERVISOR 
  INNER JOIN Employee SUPERVISEE ON  SUPERVISOR.SSN = SUPERVISEE.MSSN 
GROUP BY SuperVisor;

และผลลัพธ์คือ:

+------------+------------+----------+
| SuperVisor | SuperVisee | COUNT(*) |
+------------+------------+----------+
| A          | A,B,F      |        3 |
| B          | C,H,I,L    |        4 |
| C          | D,E        |        2 |
| F          | K          |        1 |
| H          | G          |        1 |
| I          | J          |        1 |
+------------+------------+----------+
6 rows in set (0.00 sec)

[ คำถาม ]
แทนต้นไม้ลำดับชั้นที่สมบูรณ์ฉันต้องการ a SUB-TREEจากจุด (เลือก) เช่น:
ถ้าอาร์กิวเมนต์อินพุตเป็นBเอาต์พุตก็ควรเป็นด้านล่าง ...

+------------+------------+----------+
| SuperVisor | SuperVisee | COUNT(*) |
+------------+------------+----------+
| B          | C,H,I,L    |        4 |
| C          | D,E        |        2 |
| H          | G          |        1 |
| I          | J          |        1 |
+------------+------------+----------+   

โปรดช่วยฉันในเรื่องนี้ หากไม่ใช่แบบสอบถามขั้นตอนการจัดเก็บอาจมีประโยชน์
ฉันพยายาม แต่ความพยายามทั้งหมดไร้ประโยชน์!



ฉันเพียงแค่เตรียมกรอบการทดสอบสำหรับชุมชนเพื่อใช้ในการสำรวจคำถามนี้ได้ง่ายขึ้น
mellamokb

@bluefeet ใช่เมื่อฉันจะได้รับคำตอบฉันจะลบหนึ่งในสองนี้
Grijesh Chauhan

1
@GrijeshChauhan ให้ฉันถามคุณสิ่งนี้: ไหนจะดีกว่าที่จะทำให้คลื่นที่มองเห็นของคุณเอง? การโยนก้อนกรวดลงไปในมหาสมุทรหรือโยนก้อนหินลงไปในบ่อขนาดเล็ก? การตรงไปยังผู้เชี่ยวชาญนั้นแน่นอนจะทำให้คุณได้คำตอบที่ดีที่สุดและคำถามประเภทนี้มีความสำคัญมาก (หัวข้อฐานข้อมูลขั้นสูง) ที่เราให้เว็บไซต์ของตัวเองในเครือข่าย แต่ฉันจะไม่หยุดคุณไม่ให้ถามที่คุณชอบนั่นคือสิทธิพิเศษของคุณ สิทธิพิเศษของฉันคือการลงคะแนนเพื่อย้ายไปยังเว็บไซต์อื่นหากฉันคิดว่าเป็นสิ่งที่มันเป็น : D เราทั้งคู่ใช้เครือข่ายตามที่เราเห็นว่าเหมาะสมในกรณีนี้: D
jcolebrand

1
@ jcolebrand: จริง ๆ แล้วมันเป็นความผิดของฉันเท่านั้น ฉันใช้ในการโพสต์คำถามในหลาย ๆ ด้านเพื่อให้ได้คำตอบที่ดีขึ้นรวดเร็วและมากมาย It my experience ฉันได้รับคำตอบที่ดีกว่าจากผู้เชี่ยวชาญเสมอ และฉันคิดว่าเป็นการดีกว่าที่จะย้ายคำถามไปยังผู้ดูแลฐานข้อมูล ในทุกกรณีฉันรู้สึกขอบคุณมากกับ stackoverflow และผู้ใช้ที่นี่ ฉันได้รับการแก้ไขปัญหามากมายที่หาตัวเองหรือเว็บอื่น ๆ ได้ยากมาก
Grijesh Chauhan

คำตอบ:


5

ฉันได้กล่าวถึงบางสิ่งในลักษณะนี้โดยใช้กระบวนงานที่เก็บไว้: ค้นหาระดับสูงสุดของฟิลด์ลำดับชั้น: โดยมี vs ไม่มี CTE (24 ต.ค. 2011)

ถ้าคุณดูในโพสต์ของฉันคุณสามารถใช้ฟังก์ชัน GetAncestry และ GetFamilyTree เป็นแบบจำลองสำหรับการสำรวจต้นไม้จากจุดใด ๆ

อัพเดท 2012-12-11 12:11 EDT

ผมมองย้อนกลับไปที่รหัสของฉันจากการโพสต์ของฉัน ฉันเขียนฟังก์ชัน Stored สำหรับคุณ:

DELIMITER $$

DROP FUNCTION IF EXISTS `cte_test`.`GetFamilyTree` $$
CREATE FUNCTION `cte_test`.`GetFamilyTree`(GivenName varchar(64))
RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN

    DECLARE rv,q,queue,queue_children,queue_names VARCHAR(1024);
    DECLARE queue_length,pos INT;
    DECLARE GivenSSN,front_ssn VARCHAR(64);

    SET rv = '';

    SELECT SSN INTO GivenSSN
    FROM Employee
    WHERE name = GivenName
    AND Designation <> 'OWNER';
    IF ISNULL(GivenSSN) THEN
        RETURN ev;
    END IF;

    SET queue = GivenSSN;
    SET queue_length = 1;

    WHILE queue_length > 0 DO
        IF queue_length = 1 THEN
            SET front_ssn = queue;
            SET queue = '';
        ELSE
            SET pos = LOCATE(',',queue);
            SET front_ssn = LEFT(queue,pos - 1);
            SET q = SUBSTR(queue,pos + 1);
            SET queue = q;
        END IF;
        SET queue_length = queue_length - 1;
        SELECT IFNULL(qc,'') INTO queue_children
        FROM
        (
            SELECT GROUP_CONCAT(SSN) qc FROM Employee
            WHERE MSSN = front_ssn AND Designation <> 'OWNER'
        ) A;
        SELECT IFNULL(qc,'') INTO queue_names
        FROM
        (
            SELECT GROUP_CONCAT(name) qc FROM Employee
            WHERE MSSN = front_ssn AND Designation <> 'OWNER'
        ) 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_names;
            ELSE
                SET rv = CONCAT(rv,',',queue_names);
            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 name,GetFamilyTree(name) FamilyTree
    -> FROM Employee WHERE Designation <> 'OWNER';
+------+-----------------------+
| name | FamilyTree            |
+------+-----------------------+
| A    | B,F,C,H,L,I,K,D,E,G,J |
| G    |                       |
| D    |                       |
| E    |                       |
| B    | C,H,L,I,D,E,G,J       |
| F    | K                     |
| C    | D,E                   |
| H    | G                     |
| L    |                       |
| I    | J                     |
| K    |                       |
| J    |                       |
+------+-----------------------+
12 rows in set (0.36 sec)

mysql>

มีเพียงหนึ่งจับเท่านั้น ฉันเพิ่มแถวพิเศษหนึ่งอันสำหรับเจ้าของ

  • เจ้าของมี SSN 0
  • เจ้าของเป็นเจ้านายของตัวเองด้วย MSSN 0

นี่คือข้อมูล

mysql> select * from Employee;
+-----+------+-------------+------+
| SSN | Name | Designation | MSSN |
+-----+------+-------------+------+
| 0   | A    | OWNER       | 0    |
| 1   | A    | BOSS        | 0    |
| 10  | G    | WORKER      | 5    |
| 11  | D    | WORKER      | 4    |
| 12  | E    | WORKER      | 4    |
| 2   | B    | BOSS        | 1    |
| 3   | F    | BOSS        | 1    |
| 4   | C    | BOSS        | 2    |
| 5   | H    | BOSS        | 2    |
| 6   | L    | WORKER      | 2    |
| 7   | I    | BOSS        | 2    |
| 8   | K    | WORKER      | 3    |
| 9   | J    | WORKER      | 7    |
+-----+------+-------------+------+
13 rows in set (0.00 sec)

mysql>

เข้าใจความคิด!
Grijesh Chauhan

ฉันจะปรับตัวอย่างไรเพื่อให้ลูกหลานทุกคนเป็นAแบบนี้ A A/B A/B/C A/B/C/D A/B/C/E A/B/H A/B/H/G A/B/I A/B/I/J A/B/L A/F A/F/K
Smith

มันจัดการกับมัลติโหมดได้หรือไม่? ตามที่แขวนอยู่ในฐานข้อมูลของฉันที่พบหลายโหนดของผู้ปกครอง
ع

3

สิ่งที่คุณกำลังใช้อยู่เรียกว่าถ้อยคำรายการรุ่น มันมีข้อ จำกัด มากมาย คุณจะมีปัญหาเมื่อคุณต้องการลบ / แทรกโหนดในสถานที่เฉพาะ ดีกว่าที่คุณใช้ซ้อนชุดรุ่น

มีเป็นคำอธิบายรายละเอียด น่าเสียดายที่บทความบน mysql.com ไม่มีอยู่อีกต่อไป


5
" มันมีข้อ จำกัด มากมาย " - เฉพาะเมื่อใช้ MySQL เกือบทั้งหมด DBMS รองรับการสืบค้นแบบเรียกซ้ำ (MySQL เป็นหนึ่งในไม่กี่ตัวที่ไม่ได้ทำ) และนั่นทำให้รูปแบบง่ายต่อการจัดการ
a_horse_with_no_name

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