2009-06-13 35 views
15

Tôi muốn nhận tất cả các ID từ trẻ em trong một cây chỉ có MySQL.Làm cách nào để tìm tất cả ID của trẻ em đệ quy?

Tôi có một bảng như thế này:

ID parent_id name 
1 0   cat1 
2 1   subcat1 
3 2   sub-subcat1 
4 2   sub-subcat2 
5 0   cat2 

Bây giờ tôi đang cố gắng để có được tất cả các ID con cho cat1 (2,3,4) một cách đệ quy. Có cách nào để đạt được điều đó không?

Trả lời

15

Có hai phương pháp cơ bản để thực hiện việc này: danh sách kề và danh sách lồng nhau. Hãy xem Managing Hierarchical Data in MySQL.

Những gì bạn có là danh sách kề. Không có cách nào đệ quy lấy tất cả các hậu duệ bằng một câu lệnh SQL đơn lẻ. Nếu có thể, chỉ cần lấy tất cả và bản đồ tất cả trong mã.

Bộ lồng nhau có thể làm những gì bạn muốn nhưng tôi có xu hướng tránh nó vì chi phí chèn bản ghi cao và dễ bị lỗi.

+0

Có một cách để có đệ quy trong một truy vấn SQL đơn giản trong RDBMS có hỗ trợ nó, như PostgreSQL – shesek

+0

Trang web trả lại liên kết này không có sẵn – HMagdy

1

Bạn có thể làm điều đó với quy trình được lưu trữ, nếu đó là tùy chọn cho bạn.

Nếu không, bạn không thể làm điều đó với một câu lệnh sql đơn.

Lý tưởng nhất là bạn nên thực hiện các cuộc gọi đệ quy để đi bộ cây từ chương trình

+0

Có, nhưng điều đó có thể chỉ giết hiệu suất nếu cây lớn – dm76

-1

Câu hỏi của bạn có vẻ hơi không chính xác. Tại sao bạn muốn có chúng, và bạn có ý nghĩa gì khi có chúng, "trong một cái cây"?

Bảng bạn có IS (cách quan hệ đại diện) cây.

Nếu bạn muốn chúng "trong một bảng" với các hàng chứa cặp (ID 4, ParentID 0), thì bạn cần phiên bản SQL đệ quy của công cụ SQL của bạn để làm điều này, nếu động cơ đó hỗ trợ nó.

Tôi sẽ không biết về MySQL cụ thể, nhưng sự hiểu biết của tôi là họ đã lên kế hoạch triển khai SQL đệ quy bằng cách sử dụng cú pháp tương tự như Oracle, tức là với CONNECT BY.

Nếu bạn nhìn vào mục lục thủ công của bạn cho các từ khóa như "truy vấn đệ quy" hoặc "CONNECT BY", tôi tưởng tượng bạn sẽ có thể tìm thấy câu trả lời.

(Xin lỗi vì đã không thể cung cấp một sẵn sàng để tiêu thụ nhiều câu trả lời.)

0

Thấy rằng câu trả lời là về cơ bản không có hoặc ít nhất không phải là rất dễ dàng với một tuyên bố MYSQL duy nhất, tôi sẽ đăng của tôi mã php/mysql để làm danh sách phân cấp ..

function createCategorySubArray() 
{ 
    $categories = getSQL("SELECT pos_category_id FROM pos_categories"); 
    for($i=0;$i<sizeof($categories);$i++) 
    { 
     //here we need to find all sub categories 
     $pos_category_id = $categories[$i]['pos_category_id']; 
     $cat_list[$pos_category_id] = recursiveCategory($pos_category_id,array()); 

    } 
    return $cat_list; 

} 
function recursiveCategory($pos_category_id, $array) 
{ 
    $return = getSql("SELECT pos_category_id FROM pos_categories WHERE parent = $pos_category_id"); 
    for($i=0;$i<sizeof($return);$i++) 
    { 
     $sub_cat = $return[$i]['pos_category_id']; 
     $array[] = $sub_cat; 
     $array = recursiveCategory($sub_cat, $array); 
    } 
    return $array; 
} 

Sau đó, bạn gọi nó bằng $ cat_array = createCategorySubArray();

Tôi cần điều này để tìm hiểu xem quảng cáo nào dựa trên danh mục sản phẩm đang được áp dụng cho các danh mục phụ.

1

tạo bảng cần trông giống như dưới đây

DROP TABLE IF EXISTS `parent_child`; 
CREATE TABLE `parent_child` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    `parent_id` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1; 

insert into `parent_child`(`id`,`name`,`parent_id`) 
values (1,'cat1',0),(2,'subcat1',1), 
(3,'sub-subcat1',2),(4,'sub-subcat2',2), 
(5,'cat2',0); 

Tạo chức năng để nhận phần tử con mẹ

DELIMITER $$ 

USE `yourdatabase`$$ 

DROP FUNCTION IF EXISTS `GetAllNode1`$$ 

CREATE DEFINER=`root`@`localhost` FUNCTION `GetAllNode1`(GivenID INT) RETURNS TEXT CHARSET latin1 
    DETERMINISTIC 
BEGIN 
    DECLARE rv,q,queue,queue_children TEXT; 
    DECLARE queue_length,front_id,pos INT; 
    SET rv = ''; 
    SET queue = GivenID; 
    SET queue_length = 1; 
    WHILE queue_length > 0 DO 
     SET front_id = queue; 
     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) AS qc 
     FROM `parent_child` 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$$ 

DELIMITER ; 

ghi truy vấn cho đầu ra mong muốn

01.235.
SELECT GetAllNode1(id) FROM parent_child 
or 
SELECT GetAllNode1(id) FROM parent_child where id =1 //for specific parent's child element 
6

Đây là một single-truy vấn MySql-giải pháp đơn giản:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
    SELECT @Ids := (
     SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
     FROM `table_name` 
     WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
    FROM `table_name` 
    JOIN (SELECT @Ids := <id>) temp1 
) temp2 

Chỉ cần thay <id> với yếu tố phụ huynh của ID.

Điều này sẽ trả về một chuỗi với ID s của tất cả các hậu duệ của phần tử có ID = <id>, được phân tách bằng ,. Nếu bạn muốn có nhiều hàng trở lại, với một hậu duệ trên mỗi hàng, bạn có thể sử dụng một cái gì đó như thế này:

SELECT * 
FROM `table_name` 
WHERE FIND_IN_SET(`ID`, (
    SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
     SELECT @Ids := (
      SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
      FROM `table_name` 
      WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
     FROM `table_name` 
     JOIN (SELECT @Ids := <id>) temp1 
    ) temp2 
)) 

Bao gồm các phần tử gốc/mẹ

Các OP hỏi cho trẻ em của một phần tử được trả lời ở trên. Trong một số trường hợp, có thể hữu ích khi đưa phần tử gốc/phần tử gốc vào kết quả. Dưới đây là các giải pháp đề nghị của tôi:

bằng dấu phẩy tách chuỗi id:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
    SELECT <id> Level 
    UNION 
    SELECT @Ids := (
     SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
     FROM `table_name` 
     WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
    FROM `table_name` 
    JOIN (SELECT @Ids := <id>) temp1 
) temp2 

Nhiều hàng:

SELECT * 
FROM `table_name` 
WHERE `ID` = <id> OR FIND_IN_SET(`ID`, (
    SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
     SELECT @Ids := (
      SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
      FROM `table_name` 
      WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
     FROM `table_name` 
     JOIN (SELECT @Ids := <id>) temp1 
    ) temp2 
)) 
+0

Bạn có thể giải thích giải pháp của mình không, nhưng tôi không chắc là tôi đã hiểu nó. Là @Ids một hàm bí danh/var được sử dụng đệ quy để tham gia và bạn có biết thêm thông tin về phiên bản mysql nào hỗ trợ kiểu truy vấn này hay không? – MonkeyMonkey

+0

@Ids là [biến do người dùng xác định] (http://dev.mysql.com/doc/refman/5.7/en/user-variables.html), đã tồn tại trong MySQL trong một thời gian dài. Tôi nghĩ rằng các truy vấn nên chạy trong bất kỳ phiên bản của MySQL mà bạn sẽ muốn sử dụng. Truy vấn hoạt động bằng cách ghép các id của tất cả các mục là con của mục gốc trên hàng đầu tiên, sau đó ghép các id của tất cả các mục là con của bất kỳ mục nào được bao gồm trong hàng đầu tiên trên hàng thứ hai, v.v. cuối cùng, tất cả các hàng được nối với một hàng duy nhất. Tùy chọn, hàng đó có thể được chia để mỗi mục có được hàng riêng của nó (truy vấn cuối cùng). –

+1

Điều này thật tuyệt vời và tuyệt vời, chúng tôi đã sử dụng nó một thời gian nhưng hôm nay tôi nhận ra rằng trẻ em từ cấp lồng nhau cuối cùng bị cắt bớt - phải có 'OR FIND_IN_SET (id, @Ids)' trong mệnh đề 'WHERE'. Cảm ơn! – Wirone

Các vấn đề liên quan