2017-02-19 1 views
1

Я пытаюсь реализовать Table with multiple incrementing columns which doesn't reuse deleted column values. Это сообщение было помечено как уже ответившее How auto-increment within a subset of the table MYSQL, однако ссылочный пост не соответствовал заявленным требованиям, так как он позволяет дублировать подстроечные ключи. В первом посте было дано замечание:Триггер для UPDATE и SELECT на одном столе - ОШИБКА 1235 (42000)

Create a table that will store last AI numbers per type. Use a trigger to increment it on every insert and copy to the original table. – Paul Spiegel

Я думал, что это отличная идея и реализовано.

-- MySQL Script generated by MySQL Workbench 
-- 02/19/17 08:53:34 
-- Model: New Model Version: 1.0 
SET @[email protected]@UNIQUE_CHECKS, UNIQUE_CHECKS=0; 
SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; 
SET @[email protected]@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 [email protected]_SQL_MODE; 
SET [email protected]_FOREIGN_KEY_CHECKS; 
SET [email protected]_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

Возможно, вы определили два триггера 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, но вы пытаетесь получить столбцы от t1 (NEW.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 является PRIMARY KEY в t2 мы можем использовать 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

Смежные вопросы