【问题标题】:deleting nested categories from a table从表中删除嵌套类别
【发布时间】:2011-11-26 21:40:49
【问题描述】:

我有一个名为categories 的表,看起来像这样

+-------------+--------------+---------------+
|  id         |  catName     |  parentId     |
+-------------+--------------+---------------+
|  1          | Category 1   |  0            |
|  2          | Category 2   |  0            |
|  3          | Sub Cat 1    |  1            |
|  4          | Sub Cat 2    |  1            |
|  5          | Sub sub cat 1|  4            |
|  6          | Sub sub cat 2|  4            |
|  7          | Category 2   |  0            |
+-------------+--------------+---------------+

删除一个类别及其直接子级很容易:

mysql_query("DELETE FROM `categories` WHERE `id`='$id'");
mysql_query("DELETE FROM `categories` WHERE `parentId`='$id'");

但是,我需要能够删除某个类别的所有子项。例如如果Category 1被删除Sub Cat 1Sub Cat 2Sub sub cat 1Sub sub cat 2将被删除

任何帮助表示赞赏。

【问题讨论】:

    标签: php mysql


    【解决方案1】:

    您可以使用foreign keys,这是mysql 的一个非常好的功能。这是一种用于引用不同关系的完整性检查。例如,如果您创建外键,mysql 会确保引用的条目存在。在创建外键的过程中,可以定义如果“父元素”被删除,mysql应该做什么。您可以指定“SET NULL”、“UPDATE”或“DELETE CASCADE”。这意味着,如果您删除一个类别,则每个连接的子类别也会被删除。而且由于每个子类别都是子子类别的父类别,因此也将其删除。

    【讨论】:

    • 您能详细说明一下吗?
    • 谢谢!只需要弄清楚如何使用创建外键:)
    • 您可以使用 phpMyAdmin 或查看此站点:dev.mysql.com/doc/refman/5.0/en/…
    • 我正在使用 phpMyAdmin,我刚刚将引擎从 MyISAM 切换到 innoDB,但我仍然找不到选项?? :S
    • 刚刚找到!我会告诉你进展如何
    【解决方案2】:

    这是一个简单的递归脚本,用于删除您的类别以及嵌套在其中的所有嵌套类别。

    try {
    
    $db = new PDO(DB_TYPE.':host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
    }catch( PDOException $e ) {
        echo $e->getMessage();
    }
    
    
    
    class Category {
    
    
    
    function delete_category($parrent){
        global $db;
        $stmt = $db->prepare("SELECT `id` FROM `categories` WHERE `parrentId`=:parrentId");
        $stmt->execute(array('parrentId'=>$parrent));
        while($category = $stmt->fetchObject('Category')){
            $category->delete_category($category->id);
        }
            $stmt = $db->prepare("DELETE FROM `category` WHERE `id` = :id");
            $stmt->execute(array('id'=>$parrent));
    
    }
    
    
    
    function __construct(array $data = NULL) {
        if( empty($data) )
        return;
    
        foreach( $data as $field => $value ) {
            $this->$field = $value;
        }
    }}
    

    其中 $db 是您用于连接数据库的 PDO 对象。它可能有一些错误,因为我刚刚修改了我的以适应您的问题。希望这会有所帮助。

    PS:调用你的函数,你只是这样做:$category=new Category(); $category->delete_category($_GET['id'])

    【讨论】:

      【解决方案3】:

      为了清楚起见,引用了我的原始答案。

      更新 1: 表拆分的问题在于,并不清楚子、子、类别的数量是否固定。为此,您需要拆分为与子类别一样多的表,每个子类别都有外键返回其父 ID。如果子(或子子)类别的数量是动态的,则不实用。

      在这种情况下,我认为你可以通过改变你的 表设计。如果将这两个表分成类别并 您可以利用外键的子类别 strauberry 描述的功能。例如(这些不 看起来像表格,但希望这是有道理的)括号中的东西 是解释:

      分类

      • id(主键)
      • 猫名

      子类别

      • sub_id(主键)
      • id(外键引用类别表的主键)
      • 子猫名

      然后当你创建分类表时,你可以使用 ON DELETE 级联选项。这意味着每当您删除一个类别时 从类别表中,所有相关的子类别在 MySQL 会自动为您删除 subcategories 表。 功能强大,减少您的代码,您不必确保所有 手动删除在正确的表中发生。希望澄清一个 小。

      更新 2: 这个选项的问题是,好吧,它不起作用 :) MySQL 不允许您对正在执行 DELETE 的同一个表进行子查询。所以一开始看起来很简单的问题……其实不然。

      如果您无法更改表格设计,那么您可以执行以下操作 (替换您实际要删除的 id category_id_to_delete):

      DELETE FROM categories
      WHERE id = category_id_to_delete OR id IN (
          SELECT id
          FROM categories
          WHERE parentID = category_id_to_delete
      )
      

      所以,我进行了更多检查,并在 SO MySQL: How to find all IDs of children recursively? 上遇到了类似的问题,这篇文章的评分最高的答案是 Managing Hierarchical Data in MySQL。那篇文章指出了您需要用于选择数据的带有 LEFT JOINS 的查询类型,但没有明确介绍如何删除该数据。推荐的解决方案是使用文章中与相邻集相关的查询来选择您需要的所有 id 信息,然后在您的代码(我认为是 php?)中循环这些 id 并构造一个新查询来删除它们。

      例如,如果您尝试删除类别 1 (id=1),则通过循环访问文章中 LEFT JOIN 查询中的 id,您的目标是创建另一个查询,如下所示:

      DELETE FROM categories
      WHERE id IN (1,3,4,5,6);
      

      本论坛帖子中的代码也可以帮助您在 php 中递归地执行此操作:Adjacency List Model recursive deletion

      我知道这不是一个整洁的答案,但也许这会为您指明正确的方向。

      【讨论】:

      • 现阶段我无法更改表格设计 :( 还有其他选择吗?可能是代码?
      • 上面更新了答案,但我在移动设备上,所以格式和语法可能有问题。使用前请仔细检查。上面的编辑还假设 id 是唯一的,因此 parentid 对于给定类别是唯一的。
      • hmmmm 我收到了这个错误You can't specify target table 'categories' for update in FROM clause
      • @Lee Price 是的,我明白了。 MySQL 不允许您在与 DELETE 查询相同的表上执行子查询。我会更新我的答案。再检查一件事。
      【解决方案4】:

      mysql“删除时级联”功能的演示。如果您在控制台中运行它,那么您可以一次运行所有查询,并且输出将是有意义的。如果你在一些图形工具中运行它,那么我想你必须一个一个地运行查询。

      CREATE DATABASE `recursivedemo`;
      
      USE `recursivedemo`;
      
      CREATE TABLE `categories` (
          `id` INT AUTO_INCREMENT,
          `catName` VARCHAR(50),
          `parentId` INT,
      
          PRIMARY KEY (`id`),
      
          KEY `parentId` (`parentId`),
          CONSTRAINT `categories_fk_self` FOREIGN KEY (`parentId`) REFERENCES `categories`(`id`) ON DELETE CASCADE
      ) Engine=InnoDB;
      
      SELECT * FROM `categories`;
      
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Category 1', NULL);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Category 2', NULL);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Sub Cat 1.1', 1);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Sub Cat 1.2', 1);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Sub sub cat 1.2.1', 4);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Sub sub cat 1.2.2', 4);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Sub Cat 2.1', 2);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Sub Cat 2.2', 2);
      INSERT INTO `categories`(`catName`, `parentId`) VALUES('Category 3', NULL);
      
      SELECT * FROM `categories`;
      
      DELETE FROM `categories` WHERE `id`=1;
      
      SELECT * FROM `categories`;
      
      DROP DATABASE `recursivedemo`;
      

      【讨论】:

      • 我看到这会删除类别 1 和 Sub Cat 1.1,但我看不出它会如何删除 Sub sub cat 1.2.1 和 Sub sub cat 1.2.2。也许你犯了我第一次读这个问题时犯的同样的错误? :) 删除通过 parentId 连接到顶级类别的第一级是一回事,但到达第一个子类别下方未链接到 parentId 1 的较低级别是更难的问题。也许我在你的回答中遗漏了什么?
      • @CarvellFenton 是的,您错过了“如果您在控制台中运行它,那么您可以一次运行所有查询,并且输出将有意义”部分。
      【解决方案5】:

      您可以在模型中使用以下函数来删除嵌套类别:

       public function delete_by_id($cat_id)
      {
           $this->db->delete('Categories', ['cat_id' => $cat_id]);
                  $q = $this->db->where('parent_id', $cat_id)->get('Categories');
              foreach( $q->result() as $Child ) {
                  $this->delete_by_id($Child->cat_id);
              }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-03
        • 1970-01-01
        • 2021-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-17
        • 1970-01-01
        相关资源
        最近更新 更多