考虑使用这三个表的数据库:
类别:
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中执行吗?
感谢。
答案 0 :(得分:0)
我终于解决了它。有点脏,因为我必须创建一个临时表来保存中间结果,但它可以工作。
以下是完整代码:
-- 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
答案 1 :(得分:0)
如果您将层次结构存储在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;