MySQL、PHP和PDO中高效的子代记录删除
当表递归指向自身时,从给定记录ID中删除子记录的策略是什么?具体来说,我使用的是PDO、PHP和MySQL 5.0+ 设想一个包含以下列的类别表:MySQL、PHP和PDO中高效的子代记录删除,php,mysql,sql,recursion,descendant,Php,Mysql,Sql,Recursion,Descendant,当表递归指向自身时,从给定记录ID中删除子记录的策略是什么?具体来说,我使用的是PDO、PHP和MySQL 5.0+ 设想一个包含以下列的类别表: 身份证 家长id 类别名称 如果ID为0,则它是根类别。注意,该id不是主键——可以有许多根类别 想象一下它有好几层,像食物和庇护所的根类别,然后是那些的孩子,还有那些的孩子,等等。这些是他们的后代。如果有人,比如说,删除蔬菜,那么你可以预期食物和住所将作为根类别被留下,但胡萝卜和豆类将不复存在。大厦和小木屋也会被遗弃,因为它们来自另一棵树。明白
- 身份证
- 家长id
- 类别名称
编辑:我的坏--忘记了一列--父id。这非常关键。在您的场景中可能没有选项,但是,用于存储分层数据的嵌套集模型可以使您描述的操作非常有效 这篇文章也可能有用:
在您的场景中,可能不启用选项,但是,用于存储分层数据的嵌套集模型可以使您描述的操作非常高效 这篇文章也可能有用:
尽管嵌套集模型更强大,但下面的递归示例有时已经足够好了
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){}
}
尽管嵌套集模型更强大,但有时下面的递归示例就足够了
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){}
}
一个简单的级联引用完整性应该可以做到这一点——用ON-DELETE级联声明外键。如果您索引
父id
,它甚至应该相当有效(这似乎在MySQL中是必需的;其他DBMS通常允许索引较少的FK)
例如:
CREATE TABLE your_table (
id int PRIMARY KEY,
parent_id int DEFAULT NULL,
category_name varchar(45) NOT NULL,
-- Will also create index on parent_id:
CONSTRAINT your_table_fk1 FOREIGN KEY (parent_id) REFERENCES your_table (id)
ON DELETE CASCADE
);
INSERT INTO your_table (id, category_name) VALUES (1, 'Food');
INSERT INTO your_table (id, category_name) VALUES (2, 'Shelter');
INSERT INTO your_table (id, parent_id, category_name) VALUES (3, 1, 'Vegetables');
INSERT INTO your_table (id, parent_id, category_name) VALUES (4, 3, 'Carrots');
INSERT INTO your_table (id, parent_id, category_name) VALUES (5, 3, 'Beans');
INSERT INTO your_table (id, parent_id, category_name) VALUES (7, 2, 'Mansions');
INSERT INTO your_table (id, parent_id, category_name) VALUES (8, 2, 'Cabins');
然后当你执行
DELETE FROM your_table WHERE category_name = 'Vegetables'
…不仅“蔬菜”,而且“胡萝卜”和“豆类”也将被删除
这甚至是递归的,所以
DELETE FROM your_table WHERE category_name = 'Food'
…在第一级删除“食物”,在第二级删除“蔬菜”,在第三级删除“胡萝卜”和“豆类”。一个简单的级联引用完整性应该可以做到这一点-使用ON DELETE CASCADE声明外键。如果您索引
父id
,它甚至应该相当有效(这似乎在MySQL中是必需的;其他DBMS通常允许索引较少的FK)
例如:
CREATE TABLE your_table (
id int PRIMARY KEY,
parent_id int DEFAULT NULL,
category_name varchar(45) NOT NULL,
-- Will also create index on parent_id:
CONSTRAINT your_table_fk1 FOREIGN KEY (parent_id) REFERENCES your_table (id)
ON DELETE CASCADE
);
INSERT INTO your_table (id, category_name) VALUES (1, 'Food');
INSERT INTO your_table (id, category_name) VALUES (2, 'Shelter');
INSERT INTO your_table (id, parent_id, category_name) VALUES (3, 1, 'Vegetables');
INSERT INTO your_table (id, parent_id, category_name) VALUES (4, 3, 'Carrots');
INSERT INTO your_table (id, parent_id, category_name) VALUES (5, 3, 'Beans');
INSERT INTO your_table (id, parent_id, category_name) VALUES (7, 2, 'Mansions');
INSERT INTO your_table (id, parent_id, category_name) VALUES (8, 2, 'Cabins');
然后当你执行
DELETE FROM your_table WHERE category_name = 'Vegetables'
…不仅“蔬菜”,而且“胡萝卜”和“豆类”也将被删除
这甚至是递归的,所以
DELETE FROM your_table WHERE category_name = 'Food'
…第一级删除“食物”,第二级删除“蔬菜”,第三级删除“胡萝卜”和“豆类”