树形结构与关系数据库之闭包表

在《SQL反模式》中介绍了多种把树存到关系数据库中的方法,下面介绍一下其中闭包表的用法。

建表与插入数据(SQLite3)


CREATE TABLE NODE(
id  INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
CREATE  TABLE TREE_PATH(
anc INTEGER NOT NULL, --祖先节点
des INTEGER NOT NULL, --子孙节点
pc  INTEGER NOT NULL, --是否为父子节点 1/0
PRIMARY KEY (anc,des)
);
--示例树
-- root(0)
--     |
--     A(1)
--     |___A1(2)
--     |___A2(3)
--     B(4)
--     |___B1(5)
--     |___B2(6)
--     |___C(7)
--         |___C1(8)
--         |___D(9)
--             |___E(10)


INSERT INTO NODE(id,name) VALUES (0,'ROOT');
INSERT INTO NODE(id,name) VALUES (1,'A');
INSERT INTO NODE(id,name) VALUES (2,'A1');
INSERT INTO NODE(id,name) VALUES (3,'A2');
INSERT INTO NODE(id,name) VALUES (4,'B');
INSERT INTO NODE(id,name) VALUES (5,'B1');
INSERT INTO NODE(id,name) VALUES (6,'B2');
INSERT INTO NODE(id,name) VALUES (7,'C');
INSERT INTO NODE(id,name) VALUES (8,'C1');
INSERT INTO NODE(id,name) VALUES (9,'D');
INSERT INTO NODE(id,name) VALUES (10,'E');

UPDATE sqlite_sequence SET seq=10 WHERE name='NODE';

INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,1,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,2,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,3,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,4,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,5,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,6,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,7,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,8,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,9,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (0,10,0);

INSERT INTO TREE_PATH(anc,des,pc) VALUES (1,1,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (2,2,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (3,3,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,4,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (5,5,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (6,6,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (7,7,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (8,8,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (9,9,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (10,10,0);

INSERT INTO TREE_PATH(anc,des,pc) VALUES (1,2,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (1,3,1);

INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,5,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,6,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,7,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,8,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,9,0);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (4,10,0);

INSERT INTO TREE_PATH(anc,des,pc) VALUES (7,8,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (7,9,1);
INSERT INTO TREE_PATH(anc,des,pc) VALUES (7,10,0);

INSERT INTO TREE_PATH(anc,des,pc) VALUES (9,10,1);

删除子树

假设要删除子树#7

DELETE
FROM TREE_PATH
WHERE TREE_PATH.des IN (SELECT t.des FROM TREE_PATH t WHERE t.anc=7)

移动子树

假设我们要把子树#7从节点#4移动到节点#1

1.分离子树,删除子树节点与其祖先的关系

DELETE FROM
TREE_PATH
WHERE
    des IN (SELECT d.des FROM TREE_PATH d WHERE d.anc=7)
    AND
    anc IN (SELECT a.anc FROM TREE_PATH a WHERE a.des=7 AND a.anc!=a.des)

2.将上一步分离出的子树用笛卡尔积嫁接到#1下

INSERT INTO TREE_PATH(anc,des,pc)
SELECT
    super.anc,
    sub.des,
    CASE
        WHEN
            super.anc=1 AND sub.des=7 THEN 1
        ELSE 0
    END pc
FROM
    TREE_PATH super
    CROSS JOIN
    TREE_PATH sub
WHERE
    super.des=1 AND sub.anc=7

你可能感兴趣的:(树形结构与关系数据库之闭包表)