我在MySQL中有一个项目列表,通过列“parent_id”连接。
我们假设列是:id,name,parent_id
如果我的一个用户删除了层次结构中的高项,我需要删除它的所有子项。所以,两部分问题:
1)是否存在有效且高效的MySQL调用,该调用将返回其父项不再存在的所有项目的ID?
2)在PHP中,我想我可以将这些孤立的ID从MySQL变成一个数组,然后运行一个foreach循环并删除每个?
非常感谢你的帮助。
答案 0 :(得分:4)
如果您使用的是InnoDB,则在设置外键关系时,应使用ON DELETE CASCADE
选项查看Foreign Key Constraints来处理此事。{/ p>
来自文档的示例:
CREATE TABLE parent (id INT NOT NULL,
PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE child (id INT, parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id) REFERENCES parent(id)
ON DELETE CASCADE) ENGINE=INNODB;
有了这个,如果你要添加一个父和一个匹配的子行,如下所示:
INSERT INTO parent (id) VALUES (1);
INSERT INTO child (id, parent_id) VALUES (1,1);
然后像这样删除父母:
DELETE FROM parent WHERE id = 1;
您将找到匹配的子记录。在我看来,这是最好的方法。
编辑:要在1个表格中执行此操作,您可以执行以下操作:
CREATE TABLE parent (
id INT NOT NULL,
name varchar(250) not null,
parent_id INT NULL,
FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE,
PRIMARY KEY (id)
) ENGINE=INNODB
然后,如果你添加两行,一行引用另一行:
INSERT INTO parent (id,name,parent_id)
VALUES ('1', 'Test 1', NULL), ('2', 'Test 2', '1')
然后删除两者的父行:
DELETE FROM parent WHERE id = 1;
您会发现它删除了parent_id
为1的子行。
答案 1 :(得分:2)
如果您的数据是线性层次结构(每个节点只有一个父关系),而高读取,低写入,您可以考虑将其迁移到Modified Preorder Tree Traversal结构。这听起来很吓人,但实际上非常整洁。这里有一篇很棒的文章:http://www.sitepoint.com/article/hierarchical-data-database/1/
不仅从这样的结构中检索数据非常有效(单个SELECT而且你得到所有的子项),你也可以在一个DELETE语句中删除树的整个分支。
答案 2 :(得分:1)
RIGHT JOINS通常用于在其他数据库中查找孤儿。但这也可以通过LEFT JOIN来实现。
Here is一篇关于在MySQL中获取孤儿行的非常好的文章
答案 3 :(得分:1)
我想原作者的想法是分层查询的内容。
不幸的是,MySQL没有对分层查询的原生支持(例如Oracle,你可以使用CONNECT BY
来实现你想要的东西)。
删除所有孤儿的最简单方法可能是执行如下查询:
SELECT t1.id
FROM table t1
LEFT JOIN table t2 ON t2.id = t1.parent_id
WHERE t2.id IS NULL
这会产生table
中父级不存在的所有行。
将它与一个继续执行查询并删除任何结果的PHP脚本配对,经过几次迭代后,你的表应该没有孤儿(这整个事情可能会被合并到一个你可以在一个执行的DELETE语句中while loop)。
由于传递性,您需要多次执行select-delete - 考虑孤儿是另一条记录的父级的情况;在第一次迭代中,您将删除第一个孤儿,使链中的下一个记录成为孤儿。
另外,请确保明确跳过层次结构的头部,否则最终会得到一个空表(根据定义,头部是一个孤儿)。
答案 4 :(得分:1)
另一个选项(如果您不使用InnoDB,+ 1 Paolo)是在父MySQL表上使用Delete Triggers。
请记住,使用删除触发器时,删除行由 OLD 引用。这是一个示例触发器。
DELIMITER //
CREATE TRIGGER delete_clean BEFORE DELETE ON Parent
FOR EACH ROW
BEGIN
DELETE * FROM Parent WHERE Parent.parent_id = OLD.id;
END //
DELIMITER ;