Ефективна заявка за получаване на всички дъщерни възли на дърво (mysql)

Търся ефективна заявка за връщане на дъщерните възли на дърво. Структурата на данните е:

  `ct_cid` int(10) unsigned NOT NULL default '0', // the data
  `ct_level` int(10) unsigned NOT NULL default '0', // the level (0 = topmost)
  `ct_parent` int(10) unsigned NOT NULL default '0', // parent ct_cid

Трябва да се уверя, че няма да има безкрайни цикли, дори ако дървото е счупено или два възела сочат един към друг като родител.


person Nir    schedule 08.06.2009    source източник


Отговори (3)


Вижте този запис в моя блог за това как да го направите ефективно в MySQL:

Ще трябва да създадете две функции:

CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id_with_level_and_loop(value INT, maxlevel INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
        DECLARE _id INT;
        DECLARE _parent INT;
        DECLARE _next INT;
        DECLARE _i INT;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;

        SET _parent = @id;
        SET _id = -1;
        SET _i = 0;

        IF @id IS NULL THEN
                RETURN NULL;
        END IF;

        LOOP
                SELECT  MIN(id)
                INTO    @id
                FROM    t_hierarchy
                WHERE   parent = _parent
                        AND id > _id
                        -- Checking for @start_with in descendants
                        AND id <> @start_with
                        AND COALESCE(@level < maxlevel, TRUE);
                IF @id IS NOT NULL OR _parent = @start_with THEN
                        SET @level = @level + 1;
                        RETURN @id;
                END IF;
                SET @level := @level - 1;
                SELECT  id, parent
                INTO    _id, _parent
                FROM    t_hierarchy
                WHERE   id = _parent;
                SET _i = _i + 1;
        END LOOP;
        RETURN NULL;
END

и

CREATE FUNCTION hierarchy_connect_by_iscycle(node INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
        DECLARE _id INT;
        DECLARE _loop INT;
        DECLARE _node INT;
        DECLARE EXIT HANDLER FOR NOT FOUND RETURN 0;
        SET _id = COALESCE(node, @id);
        SET _loop = 0;
        SET _node = 0;
        LOOP
                SELECT  parent
                INTO    _id
                FROM    t_hierarchy
                WHERE   id = _id;
                IF _id = @start_with THEN
                        SET _loop := _loop + 1;
                END IF;
                IF _id = COALESCE(node, @id) THEN
                        SET _node = _node + 1;
                END IF;
                IF _loop >= 2 THEN
                        RETURN _node;
                END IF;
        END LOOP;
END

който ще емулира CONNECT BY и CONNECT_BY_ISCYCLE на Oracle и ще ги използва в заявка:

SELECT  CONCAT(REPEAT('    ', lvl - 1), hi.id) AS treeitem,
        hierarchy_sys_connect_by_path('/', hi.id) AS path,
        parent, lvl,
        CASE
            WHEN lvl >= @maxlevel THEN 1
            ELSE COALESCE(
            (
            SELECT  0
            FROM    t_hierarchy hl
            WHERE   hl.parent = ho.id
                    AND hl.id <> @start_with
            LIMIT 1
            ), 1)
        END AS is_leaf,
        hierarchy_connect_by_iscycle(hi.id) AS is_cycle
FROM    (
        SELECT  hierarchy_connect_by_parent_eq_prior_id_with_level_and_loop(id, @maxlevel) AS id,
                CAST(@level AS SIGNED) AS lvl
        FROM    (
                SELECT  @start_with := 97657,
                        @id := @start_with,
                        @level := 0,
                        @maxlevel := NULL
                ) vars, t_hierarchy
        WHERE   @id IS NOT NULL
        ) ho
JOIN    t_hierarchy hi
ON      hi.id = ho.id
person Quassnoi    schedule 08.06.2009

Това е моят проблем. Рекурсивна заявка.

function select_child_ids($parent_id,&$ida) {

    global $sql;

    $items = $sql->select_column("SELECT id FROM test WHERE parent_id = ".$parent_id);

    if ($items) {
        foreach ($items as $id) {
            $ida[] = $id;
            select_child_ids($id,$ida);
        }
    } else {
        return false;
    }
}

Забележка $sql е пример за моя клас за работа с mysql. Можете да използвате своя собствена. След изпълнение можете да четете детски елементи в масив $ida

person petun    schedule 21.10.2010
comment
Мисля, че OP искаше чисто MySQL решение, а не рекурсивна PHP функция. Това вероятно все още може да бъде полезно за някои. - person John; 23.07.2015

Използвам sql заявка и след това преминавам през всички записи, за да създам необходимия набор от данни.

SELECT 
        *
    FROM 
        category
    ORDER BY 
        name ASC

тогава

while ($categoryItem = mysql_fetch_assoc($result_category))
{
    $categoryData['items'][$categoryItem['category_id']] = $categoryItem;
    $categoryData['parents'][$categoryItem['parent_id']][] = $categoryItem['category_id'];
}

Това ще ви помогне!

person TigerTiger    schedule 08.06.2009
comment
Горният пример е в PHP. Можете да извадите желано дърво, като използвате $categoryData['parents'][$category_id]. - person TigerTiger; 08.06.2009
comment
Не изглежда много ефикасно. - person Nir; 08.06.2009