考虑这种类似树的表结构:
CREATE TABLE nodes(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
parent INTEGER,
descendant_count INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY(parent) REFERENCES nodes(id) ON DELETE CASCADE
);
descendant_count
列存储后代记录的数量。
现在我通过递增每个新插入的值(或在删除时递减它)来手动维护它。基本上我一直在获取父记录,然后运行
UPDATE nodes SET descendant_count = (descendant_count + 1) ? WHERE...
在每个父母身上,直到我到达根。显然,在深层嵌套的结构上这很慢。
是否可以使用触发器来实现这一目标?或者有更快更可靠的方法吗?
更新 - 11.08.03
似乎SQLite支持recursive triggers。因此,如果我仅更新单个节点的计数,则触发器应该能够更新所有父节点上的计数:
CREATE TRIGGER setCounts AFTER UPDATE ON nodes
WHEN (NEW.descendant_count <> OLD.descendant_count)
BEGIN
-- subtract old counts
UPDATE nodes
SET descendant_count = descendant_count - OLD.descendant_count
WHERE id = NEW.parent;
-- add new counts
UPDATE nodes
SET descendant_count = descendant_count + NEW.descendant_count
WHERE id = NEW.parent;
END;
我测试了它,似乎数字是正确的,所以这毕竟是可能的吗?
答案 0 :(得分:3)
SQLite没有递归查询;你必须在你的代码中执行这个循环。
请注意,SQLite是一个嵌入式数据库,没有客户端/服务器通信开销,因此在应用程序中执行此逻辑的速度并不比在触发器中或直接在数据库中受支持的速度慢。
答案 1 :(得分:3)
您可以使用嵌套集模型。计算后代要便宜得多,但删除和插入节点要贵得多。
答案 2 :(得分:2)
您可以按如下方式优化您的解决方案。 由于更新级联递增树,这可以节省大量资金......
CREATE TRIGGER setCounts AFTER UPDATE ON nodes
WHEN (NEW.descendant_count <> OLD.descendant_count)
BEGIN
IF NEW.parent_id IS NOT NULL THEN
UPDATE nodes
SET descendant_count = descendant_count
+ NEW.descendant_count - OLD.descendant_count
WHERE id = NEW.parent;
END IF;
END;
此外,您必须处理重新分配父母的情况。
例如:
update node set parent_id = 20 WHERE parent_id = 10
为此你需要另一个触发器
CREATE TRIGGER setCounts2 AFTER UPDATE ON nodes
WHEN (NEW.parent_id <> OLD.parent_id)
BEGIN
IF OLD.parent_id IS NOT NULL THEN
UPDATE nodes SET descendant_count = descendant_count - OLD.descendant_count
WHERE id = OLD.parent;
END IF;
IF NEW.parent_id IS NOT NULL THEN
UPDATE nodes SET descendant_count = descendant_count + NEW.descendant_count
WHERE id = NEW.parent;
END IF;
END;
答案 3 :(得分:1)
在邻接列表模型(这是你正在使用的)下,很难保持表的完整性。
考虑类似nested set model的内容。某些操作的速度有一些权衡,但是对于很多操作,也有很大的性能提升。