我正在尝试根据SQL Antipatterns中给出的配方,将传递闭包表改造成当前使用MySQL的邻接列表的系统。但是,我遇到了移动子树的实现问题。
我构建了对现有系统和开发工作的闭包表的极端简化,并且一旦我以令人满意的方式工作,它将把它移植到真实数据上。我的表格如下:
CREATE TABLE `product` (
`id` BigInt( 255 ) AUTO_INCREMENT NOT NULL,
`parent` BigInt( 255 ) NOT NULL,
`description` VarChar( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY ( `id` )
)
CHARACTER SET = utf8
COLLATE = utf8_general_ci
ENGINE = InnoDB
CREATE TABLE `closure` (
`ancestor` BigInt( 255 ) NOT NULL,
`decendant` BigInt( 255 ) NOT NULL,
`depth` BigInt( 255 ) NOT NULL,
PRIMARY KEY ( `ancestor`,`decendant` )
)
CHARACTER SET = utf8
COLLATE = utf8_general_ci
ENGINE = InnoDB;
CREATE INDEX `ancestordepth` USING BTREE ON `closure`( `ancestor`, `depth` )
我的测试数据如下:
Product
=======
1,0,"Test 1"
2,0,"Test 2"
3,0,"Test 3"
4,1,"Test 4"
5,1,"Test 5"
6,1,"Test 6"
7,4,"Test 7"
8,4,"Test 8"
9,4,"Test 9"
10,7,"Test 10"
Closure
=======
1,1,0
1,4,1
1,5,1
1,6,1
1,7,2
1,8,2
1,9,2
1,10,3
2,2,0
3,3,0
4,4,0
4,7,1
4,8,1
4,9,1
4,10,2
5,5,0
6,6,0
7,7,0
7,10,1
8,8,0
9,9,0
10,10,0
我已经实现了触发器,它会在产品表中的行创建时将行插入闭包表中,并在删除产品行时从闭包表中删除行并且它们工作正常,但MySQL限制使我无法获得更新大小写(产品表中的父级更改)工作。
如果我想更新节点4,使其成为节点2的子节点而不是节点1.
SQL反模式书提供了执行此操作的查询。第一个是通过删除闭包表中的相关行来孤立现有子树。
DELETE
FROM closure
WHERE decendant IN (
SELECT decendant
FROM closure
WHERE ancestor = 4
)
AND ancestor IN (
SELECT ancestor
FROM closure
WHERE decendant = 4
AND ancestor != decendant
)
但是当然MySQL不允许你这样做,因为它的设计不足,不允许你改变你在子查询中使用的任何表。
我正在尝试将查询重新编写为自联接,因为我可以从中删除行。我已经将原始查询更改为选择而不是删除,因为它确实有效,我可以将其用作比较的基线。但是,我尝试使用连接复制查询返回一个空集。
SELECT *
FROM closure AS a
JOIN closure AS b ON a.ancestor = b.ancestor AND a.decendant = b.decendant
JOIN closure AS c ON a.ancestor = c.ancestor AND a.decendant = c.decendant
WHERE b.ancestor = 4
AND c.decendant = 4
AND c.ancestor != c.decendant
我需要能够保持执行的查询相对简单,因为它需要进入触发器。我也不能使用临时表,因为实时SQL服务器在集群中运行,我们在过去曾遇到过使用临时表破坏复制的问题。如果有人可以帮助将查询重写为允许我删除MySQL中的行的表单,我会很感激。
答案 0 :(得分:0)
以下似乎是在MySQL中正确的方法
DELETE a
FROM closure AS a
JOIN closure AS b ON a.decendant = b.decendant
LEFT JOIN closure AS c ON b.ancestor = c.ancestor and a.ancestor = c.decendant
WHERE b.ancestor = OLD.id
AND c.ancestor IS NULL;