2017-08-25 104 views
1

我有一个子表上的触发器更新父表上的计数器。无论是CREATE TRIGGER ... BEFORE INSERT还是AFTER INSERT,触发器中的SQL总是在执行后执行。我可以在实际触发语句之前执行MySQL触发器吗?

有没有办法强制它运行之前?

CREATE TABLE items (
    id int(11) unsigned NOT NULL AUTO_INCREMENT, 
    quantity_sold int(11) unsigned DEFAULT 0, 
    PRIMARY KEY (id) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE line_items (
    id int(11) unsigned NOT NULL AUTO_INCREMENT, 
    item_id int(11) unsigned DEFAULT NULL, 
    quantity int(11) unsigned DEFAULT 1, 
    PRIMARY KEY (id), 
    CONSTRAINT fk FOREIGN KEY (item_id) REFERENCES items (id) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 


INSERT INTO items (id) VALUES (1); 

DROP TRIGGER IF EXISTS line_item_trigger; 
delimiter $$ 
CREATE TRIGGER line_item_trigger BEFORE INSERT ON line_items 
FOR EACH ROW 
BEGIN 
    UPDATE items SET quantity_sold = quantity_sold + NEW.quantity WHERE id = NEW.item_id; 
END 
$$ 

运行:

INSERT INTO line_items (item_id) VALUES (1); 

...结果:

INSERT INTO line_items (item_id) VALUES (1); 
UPDATE items SET quantity_sold = quantity_sold + NEW.quantity WHERE id = NEW.item_id; 

我希望能有UPDATE第一发生。

(为什么呢?这是所有在交易完成,并且INSERT被锁定items表时它的外键检查,从而导致死锁所有的地方。)

预先感谢任何见解。

+0

你在一个事务中执行多个插入'line_items'吗? –

+0

正如我所知,在他插入新行之前触发器会首先更新您的查询。根据我的经验,这是触发器的工作原理 – Noob

+0

@ilya bursov这只是在每个事务中插入的单个行项目。 –

回答

0
CREATE TRIGGER line_item_trigger BEFORE INSERT ON line_items 
FOR EACH ROW 

该触发器被强制在插入之前调用。没有什么比这更需要做的了。

请检查您的承诺频率根据需要确保您提交交易。

+0

不确定我关注。我希望触发器的UPDATE在INSERT之前而不是之后运行。序列当前是BEGIN/INSERT/UPDATE/COMMIT,所以提交发生的频率很高。 –

+0

根据您的问题中提到的触发器,订单将为BEGIN/UPDATE/INSERT。我无法看到共享代码上的“提交”,因此我无法评论它将在何处发生。如果您已经提交,请通过查找来自数据库的死锁查询来检查导致死锁的原因。 – Steephen

0

你有没有演示这种行为的例子?

以下示例更新row_count_line_items列中的items表,从BEFORE TRIGGER开始,对line_items表中的行数进行计数。因为它是一个BEFORE TRIGGERrow_count_line_items列为零(0)(INSERT没有在那一刻做):

mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> DROP TRIGGER IF EXISTS `line_item_trigger`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> DROP TABLE IF EXISTS `line_items`, `items`; 
Query OK, 0 rows affected (0.01 sec) 

mysql> CREATE TABLE IF NOT EXISTS `items` (
    -> `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    -> `quantity_sold` int(11) unsigned DEFAULT 0, 
    -> `row_count_line_items` int, 
    -> PRIMARY KEY (`id`) 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
Query OK, 0 rows affected (0.00 sec) 

mysql> CREATE TABLE IF NOT EXISTS `line_items` (
    -> `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    -> `item_id` int(11) unsigned DEFAULT NULL, 
    -> `quantity` int(11) unsigned DEFAULT 1, 
    -> PRIMARY KEY (`id`), 
    -> CONSTRAINT `fk` FOREIGN KEY (`item_id`) REFERENCES items (`id`) 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO `items` (`id`) VALUES (1); 
Query OK, 1 row affected (0.01 sec) 

mysql> CREATE TRIGGER `line_item_trigger` BEFORE INSERT ON `line_items` 
    -> FOR EACH ROW 
    -> UPDATE `items` 
    -> SET `quantity_sold` = `quantity_sold` + NEW.`quantity`, 
    ->  `row_count_line_items` = (SELECT COUNT(`id`) 
    ->         FROM `line_items`) 
    -> WHERE `id` = NEW.`item_id`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO `line_items` (`item_id`) VALUES (1); 
Query OK, 1 row affected (0.01 sec) 

mysql> COMMIT; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT `id`, `quantity_sold`, `row_count_line_items` 
    -> FROM `items`; 
+----+---------------+----------------------+ 
| id | quantity_sold | row_count_line_items | 
+----+---------------+----------------------+ 
| 1 |    1 |     0 | 
+----+---------------+----------------------+ 
1 row in set (0.00 sec) 

mysql> SELECT `id`, `item_id`, `quantity` 
    -> FROM `line_items`; 
+----+---------+----------+ 
| id | item_id | quantity | 
+----+---------+----------+ 
| 1 |  1 |  1 | 
+----+---------+----------+ 
1 row in set (0.00 sec) 

db-fiddle

如果我们改变了事件AFTERAFTER TRIGGER),然后row_count_line_items列将有一个(1)(因为,INSERT已经完成)值:

mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> DROP TRIGGER IF EXISTS `line_item_trigger`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> DROP TABLE IF EXISTS `line_items`, `items`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> CREATE TABLE IF NOT EXISTS `items` (
    -> `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    -> `quantity_sold` int(11) unsigned DEFAULT 0, 
    -> `row_count_line_items` int, 
    -> PRIMARY KEY (`id`) 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
Query OK, 0 rows affected (0.00 sec) 

mysql> CREATE TABLE IF NOT EXISTS `line_items` (
    -> `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    -> `item_id` int(11) unsigned DEFAULT NULL, 
    -> `quantity` int(11) unsigned DEFAULT 1, 
    -> PRIMARY KEY (`id`), 
    -> CONSTRAINT `fk` FOREIGN KEY (`item_id`) REFERENCES items (`id`) 
    ->) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO `items` (`id`) VALUES (1); 
Query OK, 1 row affected (0.00 sec) 

mysql> CREATE TRIGGER `line_item_trigger` AFTER INSERT ON `line_items` 
    -> FOR EACH ROW 
    -> UPDATE `items` 
    -> SET `quantity_sold` = `quantity_sold` + NEW.`quantity`, 
    ->  `row_count_line_items` = (SELECT COUNT(`id`) 
    ->         FROM `line_items`) 
    -> WHERE `id` = NEW.`item_id`; 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO `line_items` (`item_id`) VALUES (1); 
Query OK, 1 row affected (0.08 sec) 

mysql> SELECT `id`, `quantity_sold`, `row_count_line_items` 
    -> FROM `items`; 
+----+---------------+----------------------+ 
| id | quantity_sold | row_count_line_items | 
+----+---------------+----------------------+ 
| 1 |    1 |     1 | 
+----+---------------+----------------------+ 
1 row in set (0.00 sec) 

mysql> COMMIT; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT `id`, `item_id`, `quantity` 
    -> FROM `line_items`; 
+----+---------+----------+ 
| id | item_id | quantity | 
+----+---------+----------+ 
| 1 |  1 |  1 | 
+----+---------+----------+ 
1 row in set (0.00 sec) 

db-fiddle