【问题标题】:How can I perform multi-table delete in safe mode with binary logging enabled?如何在启用二进制日志记录的安全模式下执行多表删除?
【发布时间】:2024-01-01 02:35:02
【问题描述】:

假设我有以下数据结构:

DROP TABLE IF EXISTS `A`;
DROP TABLE IF EXISTS `B`;
DROP TABLE IF EXISTS `C`;

CREATE TABLE IF NOT EXISTS `C` (
    `ID_C`
        INT UNSIGNED
        NOT NULL
        AUTO_INCREMENT,

    PRIMARY KEY (`ID_C`)
)
ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `B` (
    `ID_B`
        INT UNSIGNED
        NOT NULL
        AUTO_INCREMENT,

    `REF_C`
        INT UNSIGNED
        NOT NULL,

    PRIMARY KEY (`ID_B`),

    INDEX `FK_C` (`REF_C` ASC),

    CONSTRAINT `FK_C`
        FOREIGN KEY (`REF_C`)
        REFERENCES `C` (`ID_C`)
            ON DELETE CASCADE
            ON UPDATE CASCADE
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `A` (
    `ID_A`
        INT UNSIGNED
        NOT NULL
        AUTO_INCREMENT,

    `REF_B`
        INT UNSIGNED
        NOT NULL,

    PRIMARY KEY (`ID_A`),

    INDEX `FK_B` (`REF_B` ASC),

    CONSTRAINT `FK_B`
        FOREIGN KEY (`REF_B`)
        REFERENCES `B` (`ID_B`)
            ON DELETE CASCADE
            ON UPDATE CASCADE
) ENGINE=InnoDB;

INSERT INTO `C`
    (`ID_C`)
VALUES
    (NULL),
    (NULL);

INSERT INTO `B`
    (`ID_B`, `REF_C`)
VALUES
    (NULL, 1),
    (NULL, 1),
    (NULL, 2),
    (NULL, 2);

INSERT INTO `A`
    (`ID_A`, `REF_B`)
VALUES
    (NULL, 1),
    (NULL, 2),
    (NULL, 3),
    (NULL, 4);

B 可能有超过 3000 条记录:大约 600 条记录,引用到表 C 中的不同行。我的服务器上启用了两个设置:

SELECT
    @@SQL_SAFE_UPDATES as `safe mode`, -- result: 1
    @@LOG_BIN as `binary log`;         -- result: 1

问题: 如何有效地删除表A中的所有记录,通过表B引用表C的记录而不发出警告? em>


我尝试了什么:

DELETE
    `A`.*
FROM
    `A` INNER JOIN `B` ON `REF_B` = `ID_B`
WHERE
    `B`.`REF_C` = 1;

DBMS 服务器问题safe_mode 错误:

错误代码:1175。您正在使用安全更新模式并尝试 更新没有使用 KEY 列的 WHERE 的表禁用安全 模式,切换 Preferences -> SQL Queries 中的选项并重新连接。

我已删除 B. 别名:

DELETE
    `A`.*
FROM
    `A` INNER JOIN `B` ON `REF_B` = `ID_B`
WHERE
    `REF_C` = 1;

是的,它成功了,但我有这个:

2 行受影响,1 个警告:1592 不安全语句写入 使用语句格式的二进制日志,因为 BINLOG_FORMAT = STATEMENT。 写入具有自动递增列的表的语句 从另一个表中选择是不安全的,因为其中的顺序 检索行确定将写入哪些(如果有)行。这个 顺序无法预测,主从可能不同。

另外,我试图强制使用 PRIMARY KEY:

DELETE
    `A`.*
FROM
    `A` INNER JOIN `B` ON `REF_B` = `ID_B`
WHERE
    `A`.`ID_A` > 0
    AND
    `REF_C` = 1;

但这也没有帮助。我对我的服务器做了什么坏事或邪恶的事吗?什么方法是正确的?我错过了什么吗?

提前致谢。任何帮助将不胜感激。


P.S.:我知道如何使用 Google 和搜索栏。这是我发现的:

  1. https://*.com/questions/12275864/multiple-delete-not-working-with-inner-join

  2. http://tech-solutions4u.blogspot.ru/2012/09/mysql-multi-delete-issue-in-safe-mode.html

等等。我试过了,但最后,我不喜欢禁用服务器功能(不是我设置的)的想法,即使是“暂时......”。


编辑:

我知道,有一种解决方法可以将 GROUP_CONCAT(ID_B) 保存在临时变量中并通过它的“标量”值执行删除:

SELECT GROUP_CONCAT(`ID_B`) INTO @tmp FROM `B` WHERE `REF_C` = 1;

DELETE FROM
    `A`
WHERE
    FIND_IN_SET(`REF_B`, @tmp)
    AND
    `ID_A` > 0;

但它大约是600 * 5 = 3000 个字符,所以这个想法也不受欢迎。 我的意思是,如果没有其他可能的话,这将是最后的选择。

【问题讨论】:

    标签: mysql safe-mode binlog multi-table-delete


    【解决方案1】:

    好像有两个问题:

    1. 您已开启safe updates 模式。这是来自MySQL docs 的关于--safe-update 的示例:

      对于初学者来说,一个有用的启动选项是 --safe-updates(或 --i-am-a-dummy,具有相同的效果)。这对于您可能已经发出 DELETE FROM tbl_name 语句但忘记了 WHERE 子句的情况很有帮助。通常,这样的语句会删除表中的所有行。

      如您所见,这种模式意味着您根本不知道 SQL,并且随时准备好自取其辱。因此,此模式会在每个复杂查询上触发警告。如果您不想关闭模式,您唯一的选择是选择您要删除的所有行的 ID,然后在第二个查询中将 ID 显式传递给 delete 语句的 WHERE 子句。同时,这种方法也解决了第二个问题。

    2. error code 1592 的第二个警告与将更改记录到二进制日志中完全不同。

      错误:1592 SQLSTATE:HY000 (ER_BINLOG_UNSAFE_STATEMENT)

      消息:Statement may not be safe to log in statement format.

      我建议的上一个命令确实有一个问题,我没有指定ORDER BY 子句而是使用了LIMIT。这种情况下的顺序理论上是未定义的,因此您可以删除LIMIT 子句,或明确指定顺序:

      DELETE FROM A USING
      table_a as A
      JOIN (
        SELECT a_id
        FROM table_a as A
        JOIN table_b as B ON A.ref_b = A.b_id
        WHERE B.ref_c = 1
        ORDER BY a_id
        LIMIT 1000
      ) a_ids ON A.a_id = a_ids.a_id;
      

    【讨论】:

    • 感谢您的回复。尽管如此,还是没有成功。它也不喜欢子查询。我的尝试here.
    • 您的错误代码:1175 似乎来自 MySQL Workbench。更改首选项 -> SQL 查询并重新连接
    • 另外,语法是 SET SQL_SAFE_UPDATES=0;不是 SET @@SET SQL_SAFE_UPDATES=0;
    • DELETE USING 没有LIMIT 子句,根据docs:对于多表语法,DELETE 从每个 tbl_name 中删除满足条件的行。在这种情况下,ORDER BYLIMIT不能使用
    • @Gary 好的。不过,我能做些什么来避免二进制日志写入?
    最近更新 更多