gpt4 book ai didi

MySQL递归程序删除一条记录

转载 作者:可可西里 更新时间:2023-11-01 08:39:43 25 4
gpt4 key购买 nike

我有一个表,Models,它包含这些(相关)属性:

-- -----------------------------------------------------
-- Table `someDB`.`Models`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `someDB`.`Models` (
`model_id` MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT,
`type_id` SMALLINT UNSIGNED NOT NULL,
-- someOtherAttributes
PRIMARY KEY (`model_id`),
ENGINE = InnoDB;


+---------+---------+
| model_id| type_id |
+---------+---------+
| 1 | 4 |
| 2 | 4 |
| 3 | 5 |
| 4 | 3 |
+---------+---------+

以及显示父子关系的表 Model_Hierarchy(同样,仅显示相关属性):

-- -----------------------------------------------------
-- Table `someDB`.`Model_Hierarchy`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `someDB`.`Model_Hierarchy` (
`parent_id` MEDIUMINT UNSIGNED NOT NULL,
`child_id` MEDIUMINT UNSIGNED NOT NULL,
-- someOtherAttributes,

INDEX `fk_Model_Hierarchy_Models1_idx` (`parent_id` ASC),
INDEX `fk_Model_Hierarchy_Models2_idx` (`child_id` ASC),
PRIMARY KEY (`parent_id`, `child_id`),
CONSTRAINT `fk_Model_Hierarchy_Models1`
FOREIGN KEY (`parent_id`)
REFERENCES `someDB`.`Models` (`model_id`)
ON DELETE CASCADE
ON UPDATE NO ACTION,
CONSTRAINT `fk_Model_Hierarchy_Models2`
FOREIGN KEY (`child_id`)
REFERENCES `someDB`.`Models` (`model_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;


+-----------+----------+
| parent_id | child_id |
+-----------+----------+
| 1 | 2 |
| 2 | 4 |
| 3 | 4 |
+-----------+----------+

如果有一个模型不是另一个类型为 5 的模型的父项或子项(在某些时候),则它是无效的,因此应该被删除。

这意味着模型 12 应该被删除,因为它们在任何时候都没有作为父或子模型的 type_id = 5.

这个层级有N层,但是没有循环关系(即。1 -> 2; 2 -> 1 将不存在)。

知道如何做到这一点吗?

最佳答案

注释分散在整个代码中。

架构:

CREATE TABLE `Models`
( -- Note that for now the AUTO_INC is ripped out of this for ease of data insertion
-- otherwise we lose control at this point (this is just a test)
-- `model_id` MEDIUMINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`model_id` MEDIUMINT UNSIGNED PRIMARY KEY,
`type_id` SMALLINT UNSIGNED NOT NULL
)ENGINE = InnoDB;

CREATE TABLE `Model_Hierarchy`
( -- OP comments state these are more like components
--
-- @Drew imagine b being a product and a and c being two different ways to package it.
-- Hence b is contained in both a and c respectively and separately (ie. customer can buy
-- both a and c), however, any change (outside of the scope of this question) to b is
-- reflected to both a and c. `Model_Hierarchy can be altered, yes (the project is
-- in an early development). Max tree depth is unknown (this is for manufacturing...
-- so a component can consist of a component... that consist of further component etc.
-- no real limit). How many rows? Depends, but I don't expect it to exceed 2^32.
--
--
-- Drew's interpretation of the the above: `a` is a parent of `b`, `c` is a parent of `b`
--
`parent_id` MEDIUMINT UNSIGNED NOT NULL,
`child_id` MEDIUMINT UNSIGNED NOT NULL,

INDEX `fk_Model_Hierarchy_Models1_idx` (`parent_id` ASC),
INDEX `fk_Model_Hierarchy_Models2_idx` (`child_id` ASC),
PRIMARY KEY (`parent_id`, `child_id`),
key(`child_id`,`parent_id`), -- NoteA1 pair flipped the other way (see NoteA2 in stored proc)

CONSTRAINT `fk_Model_Hierarchy_Models1`
FOREIGN KEY (`parent_id`)
REFERENCES `Models` (`model_id`)
ON DELETE CASCADE
ON UPDATE NO ACTION,

CONSTRAINT `fk_Model_Hierarchy_Models2`
FOREIGN KEY (`child_id`)
REFERENCES `Models` (`model_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION
)ENGINE = InnoDB;

CREATE TABLE `GoodIds`
( -- a table to determine what not to delete from models
`id` int auto_increment primary key,
`model_id` MEDIUMINT UNSIGNED,
`has_been_processed` int not null,
dtFinished datetime null,
-- index section (none shown, developer chooses later, as he knows what is going on)
unique index(model_id), -- supports the "insert ignore" concept
-- FK's below:
foreign key `fk_abc_123` (model_id) references Models(model_id)
)ENGINE = InnoDB;

放下并从头开始:

-- ------------------------------------------------------------
-- reverse order is happier
drop table `GoodIds`;
drop table `Model_Hierarchy`;
drop table `Models`;
-- ------------------------------------------------------------

加载测试数据:

insert Models(model_id,type_id) values
(1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,5),(10,1),(11,1),(12,1);
-- delete from Models; -- note, truncate does not work on parents of FK's

insert Model_Hierarchy(parent_id,child_id) values
(1,2),(1,3),(1,4),(1,5),
(2,1),(2,4),(2,7),
(3,2),
(4,8),(4,9),
(5,1),
(6,1),(6,2),
(7,1),(7,10),
(8,1),(8,12),
(9,11),
(10,11),
(11,12);

-- Set 2 to test (after a truncate / copy paste of this below to up above):
(1,2),(1,3),(1,4),(1,5),
(2,1),(2,4),(2,7),
(3,2),
(4,8),(4,9),
(5,1),
(6,1),(6,2),
(7,1),(7,10),
(8,1),(8,12),
(9,1),
(10,11),
(11,12);
-- truncate table Model_Hierarchy;
-- select * from Model_Hierarchy;
-- select * from Models where type_id=5;

存储过程:

DROP PROCEDURE if exists loadUpGoodIds;
DELIMITER $$
CREATE PROCEDURE loadUpGoodIds()
BEGIN
DECLARE bDone BOOL DEFAULT FALSE;
DECLARE iSillyCounter int DEFAULT 0;

TRUNCATE TABLE GoodIds;
insert GoodIds(model_id,has_been_processed) select model_id,0 from Models where type_id=5;
WHILE bDone = FALSE DO
select min(model_id) into @the_Id_To_Process from GoodIds where has_been_processed=0;
IF @the_Id_To_Process is null THEN
SET bDone=TRUE;
ELSE
-- First, let's say this is the parent id.
-- Find the child id's that this is a parent of
-- and they qualify as A Good Id to save into our Good table
insert ignore GoodIds(model_id,has_been_processed,dtFinished)
select child_id,0,null
from Model_Hierarchy
where parent_id=@the_Id_To_Process;

-- Next, let's say this is the child id.
-- Find the parent id's that this is a child of
-- and they qualify as A Good Id to save into our Good table
insert ignore GoodIds(model_id,has_been_processed,dtFinished)
select child_id,0,null
from Model_Hierarchy
where child_id=@the_Id_To_Process;

-- NoteA2: see NoteA1 in schema
-- you can feel the need for the flipped pair composite key in the above

UPDATE GoodIds set has_been_processed=1,dtFinished=now() where model_id=@the_Id_To_Process;
END IF;

-- safety bailout during development:
SET iSillyCounter = iSillyCounter + 1;
IF iSillyCounter>10000 THEN
SET bDone=TRUE;
END IF;

END WHILE;
END$$
DELIMITER ;

测试:

call loadUpGoodIds();

-- select count(*) from GoodIds; -- 9 / 11 / 12
select * from GoodIds limit 10;
+----+----------+--------------------+---------------------+
| id | model_id | has_been_processed | dtFinished |
+----+----------+--------------------+---------------------+
| 1 | 9 | 1 | 2016-06-28 20:33:16 |
| 2 | 11 | 1 | 2016-06-28 20:33:16 |
| 4 | 12 | 1 | 2016-06-28 20:33:16 |
+----+----------+--------------------+---------------------+

Mop up调用,可以折叠到存储过程中:

-- The below is what to run
-- delete from Models where model_id not in (select null); -- this is a safe call (will never do anything)
-- the above is just a null test

delete from Models where model_id not in (select model_id from GoodIds);
-- Error 1451: Cannot delete or update a parent row: a FK constraint is unhappy
-- hey the cascades did not work, can figure that out later
-- Let go bottom up for now. Meaning, to honor FK constraints, kill bottom up.
delete from Model_Hierarchy where parent_id not in (select model_id from GoodIds);
-- 18 rows deleted
delete from Model_Hierarchy where child_id not in (select model_id from GoodIds);
-- 0 rows deleted
delete from Models where model_id not in (select model_id from GoodIds);
-- 9 rows deleted / 3 remain
select * from Models;
+----------+---------+
| model_id | type_id |
+----------+---------+
| 9 | 5 |
| 11 | 1 |
| 12 | 1 |
+----------+---------+

关于MySQL递归程序删除一条记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38078204/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com