MySQL、PHP和PDO中高效的子代记录删除

MySQL、PHP和PDO中高效的子代记录删除,php,mysql,sql,recursion,descendant,Php,Mysql,Sql,Recursion,Descendant,当表递归指向自身时,从给定记录ID中删除子记录的策略是什么?具体来说,我使用的是PDO、PHP和MySQL 5.0+ 设想一个包含以下列的类别表: 身份证 家长id 类别名称 如果ID为0,则它是根类别。注意,该id不是主键——可以有许多根类别 想象一下它有好几层,像食物和庇护所的根类别,然后是那些的孩子,还有那些的孩子,等等。这些是他们的后代。如果有人,比如说,删除蔬菜,那么你可以预期食物和住所将作为根类别被留下,但胡萝卜和豆类将不复存在。大厦和小木屋也会被遗弃,因为它们来自另一棵树。明白

当表递归指向自身时,从给定记录ID中删除子记录的策略是什么?具体来说,我使用的是PDO、PHP和MySQL 5.0+

设想一个包含以下列的类别表:

  • 身份证
  • 家长id
  • 类别名称
如果ID为0,则它是根类别。注意,该id不是主键——可以有许多根类别

想象一下它有好几层,像食物和庇护所的根类别,然后是那些的孩子,还有那些的孩子,等等。这些是他们的后代。如果有人,比如说,删除蔬菜,那么你可以预期食物和住所将作为根类别被留下,但胡萝卜和豆类将不复存在。大厦和小木屋也会被遗弃,因为它们来自另一棵树。明白了吗


编辑:我的坏--忘记了一列--父id。这非常关键。

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

这篇文章也可能有用:


在您的场景中,可能不启用选项,但是,用于存储分层数据的嵌套集模型可以使您描述的操作非常高效

这篇文章也可能有用:


尽管嵌套集模型更强大,但下面的递归示例有时已经足够好了

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){}
}

尽管嵌套集模型更强大,但有时下面的递归示例就足够了

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){}
}

一个简单的级联引用完整性应该可以做到这一点——用ON-DELETE级联声明外键。如果您索引
父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'

…在第一级删除“食物”,在第二级删除“蔬菜”,在第三级删除“胡萝卜”和“豆类”。

一个简单的级联引用完整性应该可以做到这一点-使用ON DELETE CASCADE声明外键。如果您索引
父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'
…第一级删除“食物”,第二级删除“蔬菜”,第三级删除“胡萝卜”和“豆类”