MySQL: Truy vấn phân cấp cây


20

SUB-TREE TRONG MỘT TREE trong MySQL

Trong MYSQL của tôi Database COMPANY, tôi có một Table: Employeehiệp hội đệ quy, một nhân viên có thể là ông chủ của nhân viên khác. A self relationship of kind (SuperVisor (1)- SuperVisee (∞) ).

Truy vấn để tạo bảng:

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;

Tôi đã chèn một bộ tuples (Truy vấn):

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")  

Các hàng được chèn có theo Mối quan hệ phân cấp cây :

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

Tôi đã viết một truy vấn để tìm mối quan hệ:

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;

Và đầu ra là:

+------------+------------+----------+
| 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)

[ HỎI ]
Thay vì hoàn thành Cây phân cấp, tôi cần SUB-TREEmột điểm (chọn lọc), vd:
Nếu đối số đầu vào là Bđầu ra thì nên như dưới đây ...

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

Xin hãy giúp tôi về điều này. Nếu không truy vấn, một thủ tục được lưu trữ có thể hữu ích.
Tôi đã cố gắng, nhưng mọi nỗ lực đều vô ích!


1
Fiddle

Tôi chỉ đơn giản cung cấp một khung kiểm tra để cộng đồng sử dụng để khám phá câu hỏi này dễ dàng hơn.
mellamokb

@bluefeet Có, một khi tôi nhận được câu trả lời tôi sẽ xóa một trong hai.
Grijesh Chauhan

1
@GrijeshChauhan cho tôi hỏi bạn điều này: cái nào tốt hơn để tạo sóng nhìn thấy của riêng bạn? Để ném sỏi vào đại dương, hay ném đá vào một cái ao nhỏ? Đi thẳng đến các chuyên gia gần như chắc chắn sẽ cung cấp cho bạn câu trả lời tốt nhất, và loại câu hỏi này rất quan trọng (chủ đề cơ sở dữ liệu nâng cao) mà chúng tôi đã cung cấp cho nó trang web riêng của mình trên mạng. Nhưng tôi sẽ không ngăn bạn hỏi nó ở đâu bạn thích, đó là đặc quyền của bạn. Đặc quyền của tôi là bỏ phiếu để chuyển nó sang một trang khác nếu tôi nghĩ đó là nơi nó thuộc về. : D Cả hai chúng tôi đều sử dụng mạng khi chúng tôi thấy phù hợp trong trường hợp này: D
jcolebrand

1
@jcolebrand: Thật ra đó chỉ là lỗi của tôi. Tôi sử dụng để gửi câu hỏi trên nhiều mặt để nhận được phản hồi tốt hơn, nhanh chóng và nhiều. It my experience Tôi luôn nhận được câu trả lời tốt hơn từ các chuyên gia . Và tôi nghĩ rằng đó là quyết định tốt hơn để chuyển câu hỏi đến Quản trị viên Cơ sở dữ liệu. Trong tất cả các trường hợp, tôi rất biết ơn stackoverflow và những người đang hoạt động ở đây. Tôi thực sự có giải pháp cho nhiều vấn đề rất khó tìm thấy bản thân hoặc bất kỳ trang web nào khác.
Grijesh Chauhan

Câu trả lời:


5

Tôi đã giải quyết điều gì đó có tính chất này bằng cách sử dụng Thủ tục lưu trữ: Tìm mức cao nhất của trường phân cấp: có vs không có CTE (ngày 24 tháng 10 năm 2011)

Nếu bạn xem trong bài viết của tôi, bạn có thể sử dụng các hàm GetAncestry và GetF FamilyTree làm mô hình để duyệt qua cây từ bất kỳ điểm nào.

CẬP NHẬT 2012-12-11 12:11 EDT

Tôi nhìn lại mã của tôi từ bài viết của tôi . Tôi đã viết lên Chức năng lưu trữ cho bạn:

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 $$

Nó thực sự hoạt động. Đây là một mẫu:

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>

Chỉ có một lần bắt. Tôi đã thêm một hàng cho chủ sở hữu

  • Chủ sở hữu có SSN 0
  • Chủ sở hữu là ông chủ của chính mình với MSSN 0

Đây là dữ liệu

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>

hiểu ý!
Grijesh Chauhan

Làm thế nào tôi có thể thích nghi để có được tất cả Hậu duệ Anhư thế này 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

Nó cũng xử lý nhiều nút? vì nó được treo trong cơ sở dữ liệu của tôi, nơi tìm thấy nhiều nút của cha mẹ
عثمان غني 20/12/18

3

Những gì bạn đang sử dụng được gọi là Mô hình danh sách điều chỉnh . Nó có rất nhiều hạn chế. Bạn sẽ gặp vấn đề khi bạn muốn xóa / chèn một nút tại một địa điểm cụ thể. Tốt hơn bạn nên sử dụng Nested Set Model .

Có một lời giải thích chi tiết . Thật không may, bài viết trên mysql.com không còn tồn tại nữa.


5
" nó có rất nhiều hạn chế " - nhưng chỉ khi sử dụng MySQL. Gần như tất cả DBMS đều hỗ trợ các truy vấn đệ quy (MySQL là một trong số rất ít không có) và điều đó làm cho mô hình thực sự dễ xử lý.
a_horse_with_no_name

@a_horse_with_no_name Không bao giờ sử dụng bất cứ thứ gì ngoài MySQL. Vì vậy, tôi không bao giờ biết điều đó. Cảm ơn vì thông tin.
Shiplu Mokaddim
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.