繁体   English   中英

在给定的分层数据子树中找到最低节点

[英]Finding the lowest node in a given sub-tree of hierarchical data

考虑具有以下三个表的数据库:

类别:

cat_id  name        parent_id
-----------------------
1       drinks      0
2       carbonated  1
3       cola        2
4       water       1
5       rc-cola     3

产品:

prod_id  name           default_cat
-----------------------------------
1        cola-zero      2
2        mineral water  4

cat_prod:

cat_id  prod_id
---------------
1       1
2       1
3       1
4       2

我们有类别层次结构和一个产品,可能属于几个类别。

此外,每个产品都有一个默认类别。 在这种情况下, cola-zero产品的默认类别为2 carbonated ,这是一个错误。 默认类别必须为3 cola 即,类别树中的最低类别。 但是,我可能只考虑类别树的子集:仅考虑产品所属的那些类别。

我需要更新product表中每个产品的默认类别,并确保产品的默认类别是最“定义”的类别,即给定产品的最低类别。

我可以编写一个脚本,该脚本将检索所有类别,在内存中构建树,然后针对每个产品针对该树检查默认类别。 但我希望有一种更聪明的方法,仅通过SQL来执行此操作。

甚至有可能在纯SQL中做到这一点?

谢谢。

我终于解决了。 有点脏,因为我必须创建一个临时表来保存中间结果,但是它可以工作。

这是完整的代码:

-- schema
CREATE TABLE category
(
  cat_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  parent_id INT NOT NULL,
  PRIMARY KEY (cat_id)
);

GO

CREATE TABLE product
(
  prod_id INT NOT NULL,
  name VARCHAR(255) NOT NULL,
  default_cat INT NOT NULL,
  PRIMARY KEY (prod_id)
);

GO

CREATE TABLE cat_prod
(
  cat_id INT NOT NULL,
  prod_id INT NOT NULL,
  PRIMARY KEY (cat_id, prod_id),
  FOREIGN KEY (cat_id) REFERENCES category(cat_id),
  FOREIGN KEY (prod_id) REFERENCES product(prod_id)
);

GO

-- data
INSERT INTO category (cat_id, name, parent_id)
VALUES
  (1, 'drinks', 0),
  (2, 'carbonated', 1),
  (3, 'cola', 2),
  (4, 'water', 1),
  (5, 'rc-cola', 3)
;

GO

INSERT INTO product (prod_id, name, default_cat)
VALUES
  (1, 'cola-zero', 2), -- this is a mistake! must be 3
  (2, 'mineral water', 4) -- this one should stay intact
;

GO

INSERT INTO cat_prod (cat_id, prod_id)
VALUES
  (1, 1),
  (2, 1),
  (3, 1),
  (4, 2),
  (4, 1)
;

GO

-- stored proc
CREATE PROCEDURE iterate_products()
BEGIN
    DECLARE prod_id INT;
    DECLARE default_cat INT;
    DECLARE new_default_cat INT;
    DECLARE done INT DEFAULT FALSE;
    DECLARE cur CURSOR FOR SELECT p.prod_id, p.default_cat FROM product p;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    -- temporary table to hold the category subtree for a given product
    CREATE TABLE IF NOT EXISTS tmp_category_sub_tree
    (
        cat_id INT NOT NULL,
        parent_id INT NOT NULL
    );

    OPEN cur;

    UPDATE_LOOP: LOOP
        FETCH cur INTO prod_id, default_cat;
        IF done THEN
            LEAVE UPDATE_LOOP;
        END IF;

        TRUNCATE TABLE tmp_category_sub_tree;

        -- select all cateries this products belongs to
        INSERT INTO tmp_category_sub_tree (cat_id, parent_id)
            SELECT category.cat_id, category.parent_id
            FROM category
            INNER JOIN cat_prod
              ON category.cat_id = cat_prod.cat_id
            WHERE
              cat_prod.prod_id = prod_id;

        -- select a leaf (only one)
        SELECT t1.cat_id FROM
            tmp_category_sub_tree AS t1 LEFT JOIN tmp_category_sub_tree AS t2
        ON t1.cat_id = t2.parent_id
        WHERE
            t2.cat_id IS NULL
        LIMIT 1
        INTO NEW_DEFAULT_CAT;

        -- update product record, if required
        IF default_cat != new_default_cat THEN
            UPDATE product
            SET default_cat = new_default_cat
            WHERE
                product.prod_id = prod_id;
        END IF;

    END LOOP;

    CLOSE cur;

    DROP TABLE tmp_category_sub_tree;
END;

GO

这是SQLFiddle链接: http ://sqlfiddle.com/#!2/98a45/1

如果将层次结构存储在Closure Table中 ,那么很容易找到树中最低的节点:

SELECT c.descendant FROM closure c
JOIN (SELECT MAX(pathlength) AS pathlength FROM closure) x USING (pathlength); 

找到子树的最低节点,您只需要具体说明要搜索的分支的起始节点即可:

SELECT c.descendant FROM closure c
JOIN (SELECT MAX(pathlength) AS pathlength FROM closure) x USING (pathlength)
WHERE c.ancestor = 2;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM