简体   繁体   English

以正确的顺序让父母递归从MySQL表中删除

[英]Get parents recursive in right order for deletion from MySQL table

I have an table structure like this 我有这样的表结构

mysql> SELECT id, name, parent_id FROM categories;
+-------+------------+-----------+
| id    | name       | parent_id |
+-------+------------+-----------+
| 15790 | Test       |         0 |
| 15791 | Test2      |         0 |
| 16079 | Subtest    |     15790 |
| 16080 | Subtest 2  |     15790 |
| 16081 | Subsubtest |     16079 |
+-------+------------+-----------+

Now I want to look up the parent for every children and sibling and give it back in the right order for deletion. 现在我想查找每个孩子和兄弟姐妹的父母,并按照正确的顺序将其删回以便删除。

So my output in this case would be: 所以我在这种情况下的输出是:

Array
(
  16081,
  16080,
  16079,
  15791,
  15790
)

I can't delete just by reversing the parent ids, because this should be solid walking back the tree. 我不能仅仅通过反转父ID来删除,因为这应该是坚实的走回树。

Also I am not able/allowed to change the structure of the table. 此外,我无法/允许更改表的结构。 So building kind of an index is necessary. 因此,建立一种索引是必要的。

Assuming you don't have access to TRUNCATE , SET (so you could do SET FOREIGN_KEY_CHECKS=0; ), ALTER , etc. etc., and absolutely must use a script: 假设你没有访问TRUNCATESET (所以你可以做SET FOREIGN_KEY_CHECKS=0; ), ALTER等等,绝对必须使用脚本:

Since the question is tagged with php , this should do the trick: 由于这个问题用php标记,这应该可以解决问题:

function reversetree($src_arr, $currentid = 0)
{
    $cats = array();
    foreach($src_arr as $id => $parent)
    {   
        if($parent == $currentid)
        {
            $cats[] = $id;
            $cats = array_merge($cats, reversetree($src_arr, $id));
        }
    }
    return !$currentid ? array_reverse($cats) : $cats;
}

$rs = array();
foreach($pdo->query('SELECT id, parent_id FROM categories') as $row)
    $rs[$row['id']] = $row['parent_id'];

$stmt = $pdo->prepare('DELETE FROM categories WHERE id = ?');

$pdo->beginTransaction();
foreach(reversetree($rs) as $v)
    $stmt->execute(array($v));
$pdo->commit();

I don't understand why you need the IDs in a particular order. 我不明白为什么你需要特定顺序的ID。 You can delete them with a transaction and they will all be deleted simultaneously. 您可以使用事务删除它们,它们将同时删除。

DELETE FROM categories WHERE ID IN (15790,15791,16079,16080,16081);

You could add FOREIGN KEY constraint with CASCADE on DELETE. 您可以在DELETE上使用CASCADE添加FOREIGN KEY约束。 The foreign key will point to the same table on the parent id field. 外键将指向父ID字段上的同一个表。

When you delete the parent, all the children (no matter what level) are removed automatically. 删除父项时,将自动删除所有子项(无论哪个级别)。

<?php

// housekeeping
$pdo = new PDO($dsn, $user, $password);
$select = $pdo->prepare(
    "SELECT parent.id AS parent_id, child.id AS child_id
    FROM categories AS parent
    JOIN categories AS child ON parent.id = child.parent_id
    WHERE parent.id = ?"
);
$delete = $pdo->prepare('DELETE FROM categories WHERE id = ?');

// deletes $node_id, deletes its children first if required
function delete_node($node_id){
    $select->execute( array($node_id) );
    $children = $select->fetchAll(PDO::FETCH_NUM);
    if (count($children) !== 0) { // if 0, then the category does not exist, or it has no child
        foreach ($children as $child) { // call delete_node() recursively on each child
            delete_node ($child[1]);
        }
    }
    $delete->execute( array($node_id) ); // then delete this node (or do nothing if $node_id does not exist)
}


// to delete one category and all its sub-categories

delete_node(15790);

// to delete all contents

$allTopLevel = $pdo->exec('SELECT id FROM categories WHERE parent_id = 0')->fetchAll(PDO::FETCH_NUM);
foreach ($allTopLevel as $node) {
    delete_node($node[0]);
}

Not tested, not even sure if it "compiles", but you get the idea. 没有测试,甚至不确定它是否“编译”,但你明白了。 Make sure to lock the table (or start a transaction) before calling delete_node() . 确保在调用delete_node()之前锁定表(或启动事务delete_node()

Sorry, i can't help much, because SQL is not my thing. 对不起,我帮不了多,因为SQL不是我的事。 But perhaps someone could transfer java pseudo code to the solution 但也许有人可以将java伪代码转移到解决方案中

delete(table.getFirstRow().get(id));

delete(id_a){
    for(data_set : table)
        if(data_set.get(parent_id) == id_a)delete(data_set.get(id));
    }
    table.remove(id_a);
}

edit: no iteration about elements? 编辑:没有关于元素的迭代? So something like this? 这样的事情呢?

delete(list){
    if(list.size() == 0)return;
    idList = list.getAll(id);
    plist = table.getAllWhichEquals(parent_id, idList);
    delete(plist);
    table.remove(idList);
}

ah, forget it, i'm deleting not all at the same time, was just a try ^^ 啊,忘掉它,我不是同时删除所有,只是一个尝试^^

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM