針對「刪除節點及其所有子孫節點」的經典需求,提供以 parent_id 關聯的遞迴刪除實作,並說明效能優化與替代方案。
CREATE TABLE dept (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
parent_id INT DEFAULT 0, -- 0 為根節點
INDEX idx_parent (parent_id)
);
<?php
function deleteDeptTree(PDO $pdo, int $deptId): bool {
// 1. 找直系子節點
$stmt = $pdo->prepare('SELECT id FROM dept WHERE parent_id = ?');
$stmt->execute([$deptId]);
$children = $stmt->fetchAll(PDO::FETCH_COLUMN);
// 2. 遞迴刪除子樹
foreach ($children as $childId) {
deleteDeptTree($pdo, $childId);
}
// 3. 刪除自己
$del = $pdo->prepare('DELETE FROM dept WHERE id = ?');
return $del->execute([$deptId]);
}
// 使用
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
deleteDeptTree($pdo, 5); // 刪除 id=5 及其所有下層
?>
WITH RECURSIVE to_delete AS (
SELECT id FROM dept WHERE id = 5
UNION ALL
SELECT d.id FROM dept d JOIN to_delete td ON d.parent_id = td.id
)
DELETE FROM dept WHERE id IN (SELECT id FROM to_delete);
path = '1/5/12/',刪除變成 DELETE WHERE path LIKE '1/5/12/%',單查詢完成。DELETE WHERE id IN (...)。<?php
function collectSubtreeIds(PDO $pdo, int $rootId): array {
$ids = [];
$stack = [$rootId];
while ($stack) {
$id = array_pop($stack);
$ids[] = $id;
$stmt = $pdo->prepare('SELECT id FROM dept WHERE parent_id = ?');
$stmt->execute([$id]);
$stack = array_merge($stack, $stmt->fetchAll(PDO::FETCH_COLUMN));
}
return $ids;
}
$ids = collectSubtreeIds($pdo, 5);
$pdo->exec('DELETE FROM dept WHERE id IN (' . implode(',', $ids) . ')');
?>
教學/小資料量用遞迴函式最直觀;生產環境大樹狀結構請改用 CTE、Closure Table 或批次刪除,避免效能與堆疊溢位風險。