删除层次结构中的孤立项

时间:2009-04-02 00:18:14

标签: php mysql

我在MySQL中有一个项目列表,通过列“parent_id”连接。

我们假设列是:id,name,parent_id

如果我的一个用户删除了层次结构中的高项,我需要删除它的所有子项。所以,两部分问题:

1)是否存在有效且高效的MySQL调用,该调用将返回其父项不再存在的所有项目的ID?

2)在PHP中,我想我可以将这些孤立的ID从MySQL变成一个数组,然后运行一个foreach循环并删除每个?

非常感谢你的帮助。

5 个答案:

答案 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 ;