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