MySQL 更新,但删除重复键(由于唯一索引)
MySQL update, but delete on duplicate key (due to unique index)
我正在寻找有关更新数据库 table 中多行的一些见解,这反过来会违反 table 的唯一索引。对于某些上下文:
我有一个多对多联接 table,它定义了另外两个 table 之间的关系。这个 table 上有一个唯一索引,表示子 table 外键对于单个父 table 必须是唯一的。例如:
=============================
| ID | Parent ID | Child ID |
-----------------------------
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 1 | 3 |
| 1 | 2 | 1 |
| 1 | 2 | 2 |
| 1 | 3 | 1 |
=============================
我需要弃用一些允许的 Child ID
值,并将那些现已弃用的 ID 映射到它们的非弃用值。 (例如:在上面的示例中,如果我们弃用 3
=> 2
,则会导致唯一索引违规,因为 Parent 1
和 Child 2
之间已经存在关系)
问题:
有没有一种方法可以处理唯一索引违规,只需简单地删除正在更新的当前行?
我知道 MySQL 中有一个 INSERT ON DUPLICATE KEY UPDATE
子句,但是有 UPDATE ON DUPLICATE KEY DELETE
吗?我认为我们甚至可以接受在执行更新之前删除导致唯一索引的行。 (例如:类似 UPDATE ON DUPLICATE KEY DELETE EXISTING ROW
)
只是尽量避免超级复杂的 SQL 迁移,如果可以的话,迁移更容易出错。
感谢您提供任何帮助或提示,如果您需要更多信息,请告诉我,我会尽可能多地提供。
更新:感谢 Bill 的回答,我能够想出这个 SQL 程序:
DELIMITER $$
CREATE PROCEDURE `migrate_deprecated_children`()
BEGIN
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
START TRANSACTION;
-- Insert new items, ignoring any that already exist
INSERT IGNORE INTO JOIN_TABLE (PARENT_ID, CHILD_ID)
SELECT PARENT_ID,
(CASE
WHEN CHILD_ID = 106 THEN 68
WHEN CHILD_ID IN (109, 111, 124, 131) THEN 110
WHEN CHILD_ID IN (113, 114) THEN 61
WHEN CHILD_ID = 115 THEN 48
WHEN CHILD_ID IN (118, 143, 169, 77, 86) THEN 3
WHEN CHILD_ID = 121 THEN -1
WHEN CHILD_ID = 127 THEN 102
WHEN CHILD_ID IN (134, 80) THEN 173
WHEN CHILD_ID = 136 THEN 50
WHEN CHILD_ID = 145 THEN 56
WHEN CHILD_ID = 146 THEN 0
WHEN CHILD_ID IN (14, 15) THEN 37
WHEN CHILD_ID = 54 THEN 94
WHEN CHILD_ID IN (84, 99) THEN 13
WHEN CHILD_ID = 87 THEN 83
WHEN CHILD_ID = 91 THEN 96
WHEN CHILD_ID = 98 THEN 30
ELSE CHILD_ID
END) as CHILD_ID
FROM JOIN_TABLE;
-- Delete all deprecated rows
DELETE FROM JOIN_TABLE WHERE CHILD_ID IN (14,15,54,77,80,84,86,87,91,98,99,106,109,111,113,114,115,118,121,124,127,131,134,136,143,145,146,169);
IF `_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END$$
DELIMITER ;
再次感谢!
没有 MySQL 支持的单个 SQL 语句可以满足您的描述。
最简单的解决方案是这个two-step过程:
INSERT IGNORE INTO m2mtable (id, parent_id, child_id)
SELECT id, parent_id, 2
WHERE child_id = 3;
DELETE FROM m2mtable WHERE child_id = 3;
您应该将这两个语句封装在一个事务中,这样更改对任何其他客户端来说都是原子的。
并非每个任务都必须在一个 SQL 语句中完成。
我正在寻找有关更新数据库 table 中多行的一些见解,这反过来会违反 table 的唯一索引。对于某些上下文:
我有一个多对多联接 table,它定义了另外两个 table 之间的关系。这个 table 上有一个唯一索引,表示子 table 外键对于单个父 table 必须是唯一的。例如:
=============================
| ID | Parent ID | Child ID |
-----------------------------
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 1 | 1 | 3 |
| 1 | 2 | 1 |
| 1 | 2 | 2 |
| 1 | 3 | 1 |
=============================
我需要弃用一些允许的 Child ID
值,并将那些现已弃用的 ID 映射到它们的非弃用值。 (例如:在上面的示例中,如果我们弃用 3
=> 2
,则会导致唯一索引违规,因为 Parent 1
和 Child 2
之间已经存在关系)
问题: 有没有一种方法可以处理唯一索引违规,只需简单地删除正在更新的当前行?
我知道 MySQL 中有一个 INSERT ON DUPLICATE KEY UPDATE
子句,但是有 UPDATE ON DUPLICATE KEY DELETE
吗?我认为我们甚至可以接受在执行更新之前删除导致唯一索引的行。 (例如:类似 UPDATE ON DUPLICATE KEY DELETE EXISTING ROW
)
只是尽量避免超级复杂的 SQL 迁移,如果可以的话,迁移更容易出错。
感谢您提供任何帮助或提示,如果您需要更多信息,请告诉我,我会尽可能多地提供。
更新:感谢 Bill 的回答,我能够想出这个 SQL 程序:
DELIMITER $$
CREATE PROCEDURE `migrate_deprecated_children`()
BEGIN
DECLARE `_rollback` BOOL DEFAULT 0;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
START TRANSACTION;
-- Insert new items, ignoring any that already exist
INSERT IGNORE INTO JOIN_TABLE (PARENT_ID, CHILD_ID)
SELECT PARENT_ID,
(CASE
WHEN CHILD_ID = 106 THEN 68
WHEN CHILD_ID IN (109, 111, 124, 131) THEN 110
WHEN CHILD_ID IN (113, 114) THEN 61
WHEN CHILD_ID = 115 THEN 48
WHEN CHILD_ID IN (118, 143, 169, 77, 86) THEN 3
WHEN CHILD_ID = 121 THEN -1
WHEN CHILD_ID = 127 THEN 102
WHEN CHILD_ID IN (134, 80) THEN 173
WHEN CHILD_ID = 136 THEN 50
WHEN CHILD_ID = 145 THEN 56
WHEN CHILD_ID = 146 THEN 0
WHEN CHILD_ID IN (14, 15) THEN 37
WHEN CHILD_ID = 54 THEN 94
WHEN CHILD_ID IN (84, 99) THEN 13
WHEN CHILD_ID = 87 THEN 83
WHEN CHILD_ID = 91 THEN 96
WHEN CHILD_ID = 98 THEN 30
ELSE CHILD_ID
END) as CHILD_ID
FROM JOIN_TABLE;
-- Delete all deprecated rows
DELETE FROM JOIN_TABLE WHERE CHILD_ID IN (14,15,54,77,80,84,86,87,91,98,99,106,109,111,113,114,115,118,121,124,127,131,134,136,143,145,146,169);
IF `_rollback` THEN
ROLLBACK;
ELSE
COMMIT;
END IF;
END$$
DELIMITER ;
再次感谢!
没有 MySQL 支持的单个 SQL 语句可以满足您的描述。
最简单的解决方案是这个two-step过程:
INSERT IGNORE INTO m2mtable (id, parent_id, child_id)
SELECT id, parent_id, 2
WHERE child_id = 3;
DELETE FROM m2mtable WHERE child_id = 3;
您应该将这两个语句封装在一个事务中,这样更改对任何其他客户端来说都是原子的。
并非每个任务都必须在一个 SQL 语句中完成。