MySQL,PHP和PDO中的高效后代记录删除

时间:2011-12-16 21:15:16

标签: php mysql sql recursion descendant

从表中指向某个记录ID的后代记录中删除后代记录的策略是什么?具体来说,我正在使用PDO,PHP和MySQL 5.0 +。

想象一下包含这些列的类别表:

  • ID
  • PARENT_ID
  • CATEGORY_NAME

如果ID为0,则它​​是根类别。那个id不是主键,请注意 - 可以有很多根类别。

想象一下,它有几层深层,比如食物和庇护所的根类别,然后是那些孩子,以及那些孩子,等等。这些是后代。如果有人要删除蔬菜,那么你可能会认为食物和住所会被留下作为根类别,但胡萝卜会像Beans一样消失。 Mansions and Cabins也会被遗忘,因为它们来自另一棵树。得到它?

编辑:我的错 - 忘记了一个列 - parent_id。这非常重要。

3 个答案:

答案 0 :(得分:2)

可能在你的场景中没有选项,但是,用于存储分层数据的嵌套集模型可以使你所描述的操作非常有效。

这篇文章也许有用:

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

答案 1 :(得分:1)

一个简单的级联参照完整性应该这样做 - 使用ON DELETE CASCADE声明你的FOREIGN KEY。如果你索引parent_id,它甚至应该相当高效(这似乎在MySQL中是必需的;其他DBMS通常允许无索引的FK)。

例如:

CREATE TABLE your_table (
    id int PRIMARY KEY,
    parent_id int DEFAULT NULL,
    category_name varchar(45) NOT NULL,
    -- Will also create index on parent_id:
    CONSTRAINT your_table_fk1 FOREIGN KEY (parent_id) REFERENCES your_table (id)
        ON DELETE CASCADE
);

INSERT INTO your_table (id, category_name) VALUES (1, 'Food');
INSERT INTO your_table (id, category_name) VALUES (2, 'Shelter');
INSERT INTO your_table (id, parent_id, category_name) VALUES (3, 1, 'Vegetables');
INSERT INTO your_table (id, parent_id, category_name) VALUES (4, 3, 'Carrots');
INSERT INTO your_table (id, parent_id, category_name) VALUES (5, 3, 'Beans');
INSERT INTO your_table (id, parent_id, category_name) VALUES (7, 2, 'Mansions');
INSERT INTO your_table (id, parent_id, category_name) VALUES (8, 2, 'Cabins');

然后当你执行......

DELETE FROM your_table WHERE category_name = 'Vegetables'

......不仅'蔬菜',还有'胡萝卜'和'豆'将被删除。

这甚至可以递归地进行,所以......

DELETE FROM your_table WHERE category_name = 'Food'

...删除第一级的'Food',第二级删除'Vegetables',第三级删除'Carrots'和'Beans'。

答案 2 :(得分:0)

虽然嵌套集模型功能更强大,但有时下面的递归示例也足够好。

public function deleteCategory($sCatID) {
  if (empty($sCatID)) {
    return FALSE;
  }
  // you can get your PDO database connection your own way -- this is my way for my framework
  $PDO = $this->data->mysql();
  // recursively find all the descendents of this category and delete those too
  $sSQL = "
  SELECT
    `id`
  FROM
    `categories`
  WHERE
    `parent_id` = :parent_id;
  ";
  $st = $PDO->prepare($sSQL);
  $st->bindValue(':parent_id',$sCatID);
  try {
    $st->execute();
    $rsRows = $st->fetchAll();
    foreach($rsRows as $rwRow) {
      $sChildCatID = $rwRow['id'];
      // note the recursion here!
      $this->deleteCategory($sChildCatID);
    }
  } catch (PDOException $e) {}
  unset($st);
  // now delete this category
  $sSQL = "
  DELETE FROM
    `categories`
  WHERE
    `id` = :id
  LIMIT 1;
  ";
  $st = $PDO->prepare($sSQL);
  $st->bindValue(':id',$sCatID);
  try {
    $st->execute();
  } catch (PDOException $e){}
}