2011-10-03 54 views
17

全部,通過事先等價的MySQL連接

我有一個表中的三個字段定義了MySQL數據庫版本5.0中存在的父子關係。表名是tb_Tree,它具有以下數據:

Table Name: tb_Tree 

Id | ParentId | Name 
-------------------- 
1 | 0  | Fruits 
2 | 0  | Vegetables 
3 | 1  | Apple 
4 | 1  | Orange 
5 | 2  | Cabbage 
6 | 2  | Eggplant 

我如何編寫一個查詢,如果指定的ParentId是讓所有的孩子們。請注意,給出的表格條目僅僅是示例數據,它們可以有更多的行。 Oracle有一個「CONNECT BY PRIOR」子句,但我沒有發現任何類似於MySQL的東西。任何人都可以請指教?

感謝

+0

我覺得 「WITH」 可以幫助您與遞歸查詢。 – FUD

+0

請你詳細說明一下嗎?謝謝 – Jake

+1

MySQL沒有CTE('WITH') –

回答

10

MySQL不支持遞歸查詢,所以你必須做它堅硬方式:

  1. 選取ParentID = X其中X是你的根行。
  2. 收集(1)中的Id值。
  3. 對於來自(2)的每個Id重複(1)。
  4. 繼續用手遞歸,直到找到所有的葉節點。

如果你知道最大深度,那麼你可以將你的表連接到自己(使用LEFT OUTER JOINs)到最大可能深度,然後清理NULL。

您也可以將您的樹表示更改爲nested sets

1

這是一個古老的線程,但因爲我在另一個論壇得到了這個問題,我想我'd在這裏添加它。對於這種情況,我創建了一個硬編碼的存儲過程來處理特定情況。這當然有一些缺點,因爲並不是所有用戶都可以隨意創建存儲過程,但是。

考慮下面的表節點和兒童:

CREATE TABLE nodes (
     parent INT, 
     child INT 
); 

INSERT INTO nodes VALUES 
     (5, 2), (5, 3), 
     (18, 11), (18, 7), 
     (17, 9), (17, 8), 
     (26, 13), (26, 1), (26,12), 
     (15, 10), (15, 5),  
     (38, 15), (38, 17), (38, 6), 
     (NULL, 38), (NULL, 26), (NULL, 18); 

有了這張表,以下存儲過程將計算結果集,包括所提供的該節點的所有死者的:

delimiter $$ 
CREATE PROCEDURE find_parts(seed INT) 
BEGIN 
    -- Temporary storage 
    DROP TABLE IF EXISTS _result; 
    CREATE TEMPORARY TABLE _result (node INT PRIMARY KEY); 

    -- Seeding 
    INSERT INTO _result VALUES (seed); 

    -- Iteration 
    DROP TABLE IF EXISTS _tmp; 
    CREATE TEMPORARY TABLE _tmp LIKE _result; 
    REPEAT 
    TRUNCATE TABLE _tmp; 
    INSERT INTO _tmp SELECT child AS node 
     FROM _result JOIN nodes ON node = parent; 

    INSERT IGNORE INTO _result SELECT node FROM _tmp; 
    UNTIL ROW_COUNT() = 0 
    END REPEAT; 
    DROP TABLE _tmp; 
    SELECT * FROM _result; 
END $$ 
delimiter ; 
1

以下select列出所有植物和它們的parentid高達4級(當然,您可以延長級別):

select id, name, parentid 
,(select parentid from tb_tree where id=t.parentid) parentid2 
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3 
,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4 
from tb_tree t 

然後您可以使用此查詢來獲得最終結果。例如,您可以通過以下sql獲取「Fruits」的所有子項:

select id ,name from (
    select id, name, parentid 
    ,(select parentid from tb_tree where id=t.parentid) parentid2 
    ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3 
    ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4 
    from tb_tree t) tt 
where ifnull(parentid4,0)=1 or ifnull(parentid3,0)=1 or ifnull(parentid2,0)=1 or ifnull(parentid,0)=1 
0

下面的存儲過程對具有反向引用的行進行排序。注意第一步我將行復制到臨時表中 - 這些行匹配一些條件。在我的情況下,這些行屬於同一個線性(GPS導航中使用的道路)。業務領域並不重要。就我而言,我正在排序屬於同一條道路的路段

DROP PROCEDURE IF EXISTS orderLocations; DELIMITER //

CREATE PROCEDURE orderLocations(_full_linear_code VARCHAR(11)) BEGIN

DECLARE _code VARCHAR(11); 
DECLARE _id INT(4); 
DECLARE _count INT(4); 
DECLARE _pos INT(4); 

DROP TEMPORARY TABLE IF EXISTS temp_sort; 

CREATE TEMPORARY TABLE temp_sort (
    id    INT(4) PRIMARY KEY, 
    pos    INT(4), 
    code   VARCHAR(11), 
    prev_code  VARCHAR(11) 
); 

-- copy all records to sort into temp table - this way sorting would go all in memory 
INSERT INTO temp_sort SELECT 
         id, -- this is primary key of original table 
         NULL, -- this is position that still to be calculated 
         full_tmc_code, -- this is a column that references sorted by 
         negative_offset -- this is a reference to the previous record (will be blank for the first) 
         FROM tmc_file_location 
         WHERE linear_full_tmc_code = _full_linear_code; 

-- this is how many records we have to sort/update position 
SELECT count(*) 
FROM temp_sort 
INTO _count; 

-- first position index 
SET _pos = 1; 

-- pick first record that has no prior record 
SELECT 
    code, 
    id 
FROM temp_sort l 
WHERE prev_code IS NULL 
INTO _code, _id; 

-- update position of the first record 
UPDATE temp_sort 
SET pos = _pos 
WHERE id = _id; 

-- all other go by chain link 
WHILE (_pos < _count) DO 
    SET _pos = _pos +1; 

    SELECT 
    code, 
    id 
    FROM temp_sort 
    WHERE prev_code = _code 
    INTO _code, _id; 


    UPDATE temp_sort 
    SET pos = _pos 
    WHERE id = _id; 

END WHILE; 

-- join two tables and return position along with all other fields 
SELECT 
    t.pos, 
    l.* 
FROM tmc_file_location l, temp_sort t 
WHERE t.id = l.id 
ORDER BY t.pos; 

END;