【问题标题】:How can I perform exception handling in MySQL (referential integrity)如何在 MySQL 中执行异常处理(参照完整性)
【发布时间】:2013-08-29 23:14:18
【问题描述】:

MySQL version: 5.6.12

大家好,

我有一个 MySQL 数据库,下面有几个表和外键。由于修订目的,我需要通过在表的已删除字段中填写删除时间戳来对记录进行软删除(null 表示未删除)。现在我想创建一个触发器,如果​​它们的父项被删除,则取消链接已删除的项目。为了澄清,我做了一个小演示脚本:

表 arnold_test.Customer:

CREATE SCHEMA IF NOT EXISTS `arnold_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `arnold_test` ;

-- -----------------------------------------------------
-- Table `arnold_test`.`Customer`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `arnold_test`.`Customer` (
  `CustomerId` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `Name` VARCHAR(45) NULL,
  `Email` VARCHAR(45) NULL,
  `Phone` VARCHAR(45) NULL,
  `Deleted` DATETIME NULL,
  PRIMARY KEY (`CustomerId`))
ENGINE = InnoDB;

表 arnold_test.Address

-- -----------------------------------------------------
-- Table `arnold_test`.`Address`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `arnold_test`.`Address` (
  `AddressId` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `CustomerId` INT UNSIGNED NOT NULL,
  `Street` VARCHAR(45) NULL,
  `ZipCode` VARCHAR(45) NULL,
  `State` VARCHAR(45) NULL,
  `City` VARCHAR(45) NULL,
  `Country` VARCHAR(45) NULL,
  `Deleted` DATETIME NULL,
  PRIMARY KEY (`AddressId`),
  INDEX `fk_Address_Customer_idx` (`CustomerId` ASC),
  CONSTRAINT `fk_Address_Customer`
    FOREIGN KEY (`CustomerId`)
    REFERENCES `arnold_test`.`Customer` (`CustomerId`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

删除触发器:

USE `arnold_test`;

DELIMITER $$
USE `arnold_test`$$
CREATE
DEFINER=`root`@`%`
TRIGGER `arnold_test`.`NinjaDeleteTrigger_Customer`
BEFORE UPDATE ON `arnold_test`.`Customer`
FOR EACH ROW
BEGIN

    IF OLD.Deleted IS NULL AND NEW.Deleted IS NOT NULL THEN


        BEGIN


            DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' BEGIN
                UPDATE `arnold_test`.`Address` R
                SET    R.Deleted = NEW.Deleted
                WHERE
                    OLD.CustomerId = R.CustomerId            
                    AND R.Deleted IS NULL;
            END;

            UPDATE `arnold_test`.`Address` R
            SET
                R.CustomerId = NULL        
            WHERE
                NEW.CustomerId = R.CustomerId        
                AND R.Deleted IS NULL;


        END;



    END IF;

END$$


DELIMITER ;

现在的问题是,当我尝试从 Customer 表中删除一条记录时(通过使用 UPDATE .. SET Deleted = UTC_TIMESTAMP ..;),由于以下错误,查询失败:

ERROR 1452: 1452: Cannot add or update a child row: a foreign key constraint fails (`arnold_test`.`Address`, CONSTRAINT `fk_Address_Customer` FOREIGN KEY (`CustomerId`) REFERENCES `Customer` (`CustomerId`) ON DELETE NO ACTION ON UPDATE NO ACTION)

当我使用 GET DIAGNOSTICS CONDITION 1 @p1 = RETURNED_SQLSTATE; SELECT @p1; 检索 SQLSTATE 代码时,响应为 23000(它在触发器中的 continue 处理程序中引用 sqlstate)。我不知道我做错了什么。

我在互联网上搜索了 MySQL 中的异常处理,但找不到关键答案。有人有什么想法吗?

感谢您的帮助! :-)

亲切的问候, 阿诺德·皮斯托瑞斯

【问题讨论】:

  • @Varun 我试过了,是的(如果你阅读了我的触发脚本,你会发现我的代码中已经有了它)。我还尝试捕获 SQLEXCEPTION 和 SQLWARNING,但没有得到预期的结果。

标签: mysql sql exception-handling


【解决方案1】:

不能设置CustomerId = null,因为地址表的customerid列是FOREIGN键和customer表,customerid列是主键,主键没有空值。

        UPDATE `Address` R
        SET
        R.CustomerId = NULL 

【讨论】:

  • 如果 Address.CustomerId 为 Nullable,这实际上是可能的。但是我有一个 PHP 脚本,它为所有引用的表生成通用触发器。因此,如果 Referenced 字段不可为空,那么我想将记录设置为已删除,而不是对 null 的引用。我认为应该可以“捕获”错误并继续使用备用脚本。
【解决方案2】:

我选择重写我的 PHP 脚本。现在它检查引用的字段是否为 NULLABLE。如果是,则将引用设置为 NULL,否则将删除整条记录。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    • 2011-09-17
    相关资源
    最近更新 更多