Получить рекурсивные родители в правильном порядке для удаления из таблицы MySQL

у меня есть структура таблицы, как это

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 |
+-------+------------+-----------+

теперь я хочу найти родителя для каждого ребенка и брата и брата и вернуть его в правильном порядке для удаления.

поэтому мой вывод в этом случае будет:

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

Я не могу удалить, просто изменив родительские идентификаторы, потому что это должно быть твердым возвращением дерева.

также я не могу / не могу изменить структуру таблицы. Так дом вроде индекса необходимый.

5 ответов


полагаю, у вас нет доступа к TRUNCATE, SET (Так что вы могли бы сделать SET FOREIGN_KEY_CHECKS=0;),ALTER, etc. так далее. и абсолютно должны использовать скрипт:

так как вопрос с тегами 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();

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

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

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

когда вы удаляете родителя, все дети (независимо от того, какой уровень) удаляются автоматически.


<?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]);
}

не проверено, даже не уверен, что он "компилируется", но вы получаете идею. Обязательно заблокируйте таблицу (или запустите транзакцию) перед вызовом delete_node().


Извините,я не могу помочь, потому что SQL-это не мое. Но, возможно, кто-то может передать 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: нет итерации об элементах? Что-то вроде этого?

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

ах, забудьте об этом, я удаляю не все одновременно, была просто попытка ^^