在同一个表上触发UPDATE和SELECT - ERROR 1235(42000)

时间:2017-02-19 17:09:27

标签: mysql triggers

我正在尝试实施Table with multiple incrementing columns which doesn't reuse deleted column values。这篇文章被标记为How auto-increment within a subset of the table MYSQL已经回答,但是,引用的帖子不符合规定的要求,因为它允许重复子集递增的密钥。在第一篇文章中,给出了评论:

  

创建一个表格,用于存储每种类型的最后AI编号。使用触发器   在每个插入上增加它并复制到原始表。 - 保罗   明镜

我认为这是一个好主意,并实施了它。

-- MySQL Script generated by MySQL Workbench
-- 02/19/17 08:53:34
-- Model: New Model    Version: 1.0
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`t1`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`t1` (
  `pk1` INT NOT NULL,
  `pk2` INT NOT NULL,
  `id2` INT NOT NULL DEFAULT 0,
  PRIMARY KEY (`pk1`, `pk2`))
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`t2`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`t2` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `id2` INT NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
USE `mydb`;

DELIMITER $$
USE `mydb`$$
CREATE TRIGGER `t2_BINS` BEFORE INSERT ON `t2` FOR EACH ROW
begin
UPDATE t1 SET id2=id2+1 WHERE pk1=NEW.pk1 AND pk2=NEW.pk2;
SET NEW.id2=(SELECT id2 FROM t1 WHERE pk1=NEW.pk1 AND pk2=NEW.pk2);
end$$


DELIMITER ;

但是,在执行脚本时,我收到以下错误:

ERROR 1235 (42000) at line 68: This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table'

我正在使用MySQL 5.5.54。是否有可以这样做的新版本?

是否有任何解决方法可以实现此目的?

1 个答案:

答案 0 :(得分:1)

您可能在一个表上定义了两个BEFORE INSERT触发器。至少这是错误信息所说的。取消注释this demo中的第二个触发器,您将收到相同的错误消息

This version of MySQL doesn't yet support 'multiple triggers
with the same action time and event for one table'

第二个错误是您在错误的表上定义了触发器(或者您已将触发器中的表混为一谈)。您的触发器位于t2,但您尝试访问t1NEW.pk1)中的列。因此,您将收到以下错误消息:

Unknown column 'pk1' in 'NEW'

http://sqlfiddle.com/#!9/1515abb(取消注释触发器中的行。)

然而 - 即使修复它,逻辑看起来也不正确。您可能想要的工作版本可能是:

CREATE TABLE IF NOT EXISTS `t1` (
  `pk1` INT NOT NULL,
  `pk2` INT NOT NULL DEFAULT 0,
  PRIMARY KEY (`pk1`, `pk2`))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `t2` (
  `pk1` INT NOT NULL,
  `last_pk2` INT NOT NULL,
  PRIMARY KEY (`pk1`))
ENGINE = InnoDB;

DELIMITER //

CREATE TRIGGER `t1_BINS` BEFORE INSERT ON `t1` FOR EACH ROW
begin
  UPDATE t2 SET last_pk2=last_pk2 + 1 WHERE pk1=NEW.pk1;
  SET NEW.pk2=(SELECT last_pk2 FROM t2 WHERE pk1=NEW.pk1);
end//

DELIMITER ;

正如您在demo中看到的那样,第二个插入内容未指定pk2,但结果会增加。

如果您插入未在pk1中注册的t2值,则会失败。 (取消注释demo中的最后一行)在这种情况下,必须在t2中插入一个新行。由于pk1中的t2是PRIMARY KEY,我们可以使用INSERT .. ON DUPLICATE KEY UPDATE ..语法:

CREATE TRIGGER `t1_BINS` BEFORE INSERT ON `t1` FOR EACH ROW
begin
  INSERT INTO t2 (pk1, last_pk2) values (NEW.pk1, 1)
    ON DUPLICATE KEY UPDATE last_pk2 = last_pk2 + 1;
  SET NEW.pk2=(SELECT last_pk2 FROM t2 WHERE pk1=NEW.pk1);
end//

http://sqlfiddle.com/#!9/79eeea/1

现在最后一步是使其并发保存。我们必须防止两个并发线程/会话为t2.last_pk2读取相同的值。因此,写作和阅读应该在一个声明中完成。我们可以使用会话变量或LAST_INSERT_ID()来执行此操作:

CREATE TRIGGER `t1_BINS` BEFORE INSERT ON `t1` FOR EACH ROW
begin
  INSERT INTO t2 (pk1, last_pk2) values (NEW.pk1, @last_pk2 := 1)
    ON DUPLICATE KEY UPDATE last_pk2 = @last_pk2 := (last_pk2 + 1);
  SET NEW.pk2 = @last_pk2;
end//

http://sqlfiddle.com/#!9/dc235a/1

CREATE TRIGGER `t1_BINS` BEFORE INSERT ON `t1` FOR EACH ROW
begin
  INSERT INTO t2 (pk1, last_pk2) values (NEW.pk1, LAST_INSERT_ID(1))
    ON DUPLICATE KEY UPDATE last_pk2 = LAST_INSERT_ID(last_pk2 + 1);
  SET NEW.pk2 = LAST_INSERT_ID();
end//

http://sqlfiddle.com/#!9/86ed740/1