Эффективное удаление записей потомков в MySQL, PHP и PDO - PullRequest
0 голосов
/ 17 декабря 2011

Какова стратегия удаления дочерних записей из заданного идентификатора записи, когда таблица рекурсивно указывает на себя? В частности, я использую PDO, PHP и MySQL 5.0 +.

Представьте себе таблицу категорий с этими столбцами:

  • id
  • parent_id
  • имя_категории

Если идентификатор равен 0, то этокорневая категория.Имейте в виду, что этот идентификатор не является первичным ключом - корневых категорий может быть много.

Представьте, что он имеет несколько уровней глубины, например, корневые категории "Пища и Укрытие", а затем их потомки и потомки тех, искоро.Это потомки.Если бы кто-то, скажем, удалил Овощи, то можно было бы ожидать, что Еда и Укрытие останутся корневыми категориями, но Морковь исчезнет, ​​как и Бобы.Особняки и Каюты также будут оставлены позади, потому что они с другого дерева.Получите?

РЕДАКТИРОВАТЬ : Мой плохой - забыл столбец - parent_id.Это очень важно.

Ответы [ 3 ]

2 голосов
/ 17 декабря 2011

Возможно, в вашем сценарии опция недоступна, однако модель вложенного набора для хранения иерархических данных может сделать операции, подобные описанной вами, очень эффективными.

Также эта статья может быть полезна:

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

1 голос
/ 18 декабря 2011

Простая каскадная ссылочная целостность должна сделать это - объявите свой FOREIGN KEY с помощью ON DELETE CASCADE.И если вы индексируете parent_id, он должен быть даже достаточно эффективным (это, по-видимому, в любом случае требуется в MySQL; другие СУБД обычно допускают FK без индекса).*

Затем, когда вы выполните ...

DELETE FROM your_table WHERE category_name = 'Vegetables'

... будут удалены не только "Овощи", но также "Морковь" и "Бобы".

Это дажеработает рекурсивно, поэтому ...

DELETE FROM your_table WHERE category_name = 'Food'

... удаляет «Еда» на первом уровне, «Овощи» на втором и «Морковь» и «Бобы» на третьем.

0 голосов
/ 17 декабря 2011

Хотя модель с вложенным множеством является более мощной, иногда может быть достаточно следующего примера с рекурсией.

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