web-dev-qa-db-ja.com

MySQLトリガーを使用して新しいレコードを挿入した後にテーブル列を更新する

次のフィールドを持つMySQLテーブル(tbl_test)があると想像してください:id、title、priority
idは自動的に増分されます。挿入後にpriorityフィールドにidフィールドと同じ値を入力する必要があります。
MySQLトリガーを使用するのが初めてなので、何を書く必要があるか教えてください。私は何かをしましたが、それは真実ではないと思います:

CREATE TRIGGER 'test' AFTER INSERT ON `tbl_test`
BEGIN
   SET new.priority = new.id;
END

ご協力ありがとうございます。

13
Mohammad Saberi

列に値を設定する方法は更新です。あなたがそれをしているので挿入後操作が完了しました。

実際にはbeforeトリガーが必要です。

また、同じテーブルの主キー列の同じ新しい自動インクリメント値を割り当てるには、_information_schema.tables_から取得する方が適切です。

_delimiter //
drop trigger if exists bi_table_name //

create trigger bi_table_name before insert on table_name
for each row begin
  set @auto_id := ( SELECT AUTO_INCREMENT 
                    FROM INFORMATION_SCHEMA.TABLES
                    WHERE TABLE_NAME='table_name'
                      AND TABLE_SCHEMA=DATABASE() ); 
  set new.priority= @auto_id;
end;
//

delimiter ;
_

:同じ名前またはアクション、あるいはその両方を持つ定義済みのトリガーがないことを確認してください。いくつかある場合は、新しいものを作成する前に削除してください。

観察
mysqlドキュメント(last_insert_id() による

"単一のINSERTステートメントを使用して複数の行を挿入した場合、LAST_INSERT_ID()は最初に挿入された行に対してのみ生成された値を返します。"

したがって、バッチ挿入のlast_insert_id()および_auto_increment_フィールド値に依存することは信頼できないようです。

17
Ravinder Reddy

そんなことはできないと思います。 AFTER INSERTトリガーは、UPDATEを発行することによっても、次のようなものによっても、同じテーブルを変更できません。

DROP TRIGGER new_tbl_test;

DELIMITER $$

CREATE TRIGGER new_tbl_test 
AFTER INSERT ON tbl_test for each row
begin
UPDATE tbl_test SET priority = new.id WHERE id = new.id;
END $$

DELIMITER ;

それはのようなエラーを与えます

ERROR 1442 (HY000): Can't update table 'tbl_test' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.

あなたができることは、トランザクションを使うことです:

例:テーブル構造は以下のようになります

mysql> show create table tbl_test\G
*************************** 1. row ***************************
       Table: tbl_test
Create Table: CREATE TABLE `tbl_test` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `title` char(30) DEFAULT NULL,
  `priority` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

トランザクション

START TRANSACTION ;
  INSERT INTO tbl_test (title)
    VALUES ('Dr');
  UPDATE tbl_test
    SET `priority` = id
    WHERE id = LAST_INSERT_ID();
COMMIT ;

データをチェック

mysql> SELECT * FROM tbl_test;
+----+-------+----------+
| ID | title | priority |
+----+-------+----------+
|  1 | Dr    |        1 |
+----+-------+----------+
1 row in set (0.00 sec)
3
Abdul Manaf

私の解決策。最初は、navOrder列にテーブルのuniqueIDが必要でした。しかし、一括挿入のトリガー中に問題のテーブルのuniqueIDを取得する問題は、問題が多すぎました。そのため、MSSQLのROWNUMBER機能と同様のメカニズムを構築しました。この場合、行が含まれているテーブルやスキーマに関係なく、すべての行に番号が付けられます。トリガーはすべてのテーブルに追加されました)、すべての行に一意の値が必要になるという私の問題を解決します。

したがって、2つのテーブルがあり、それぞれに次の一意の連続番号を取得するユーザー定義関数(UDF)を呼び出すBEFORE INSERTトリガーがあります。

私は機能を一括テストしました(1つのクエリに1,000行を挿入し、1,000クエリを実行し、それらすべてを10回並列で実行します)。これを、毎分約2,000のリアルタイムユーザーと毎分約15,000の挿入を経験するサイトに使用します。 Facebookではありませんが、私たちはこれをすべて使用しており、それが私たちのために働いています。

以下のコードを実行すると、ロールバックによって連番がロールバックされないことがわかります。

デッドロックや重複値はありません(navOrder列の一意制約では重複が許可されていないため)。

私にとってこれは比較的理解しやすい解決策です。

_CREATE SCHEMA TestLastInsertId;
USE TestLastInsertId;

CREATE TABLE Table1 (
    `tempID`    INT(11)        NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `item`      VARCHAR(256)   NOT NULL,
    `navOrder`  INT(11) UNIQUE NOT NULL,
    `createdAt` TIMESTAMP(6)   NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    `updatedAt` TIMESTAMP(6)   NULL     DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(6)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci AUTO_INCREMENT = 1;

CREATE TABLE Table2 (
    `tempID`    INT(11)        NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `item`      VARCHAR(256)   NOT NULL,
    `navOrder`  INT(11) UNIQUE NOT NULL,
    `createdAt` TIMESTAMP(6)   NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
    `updatedAt` TIMESTAMP(6)   NULL     DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(6)
) ENGINE = INNODB DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci AUTO_INCREMENT = 1;

CREATE TABLE IF NOT EXISTS `nav_order_sequence` (
    `navOrderSequence` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

DELIMITER ;;

CREATE FUNCTION getNextNavOrder()
  RETURNS INT(11) LANGUAGE SQL NOT DETERMINISTIC MODIFIES SQL DATA SQL SECURITY INVOKER
  BEGIN
    INSERT INTO nav_order_sequence() VALUES();
    SET @navOrder = LAST_INSERT_ID();
    DELETE FROM nav_order_sequence WHERE navOrderSequence = @navOrder;
    RETURN @navOrder;
  END;;

CREATE TRIGGER Table1_BEFORE_INSERT BEFORE INSERT ON Table1 FOR EACH ROW
BEGIN
SET NEW.navOrder = getNextNavOrder();
END;;

CREATE TRIGGER Table2_BEFORE_INSERT BEFORE INSERT ON Table2 FOR EACH ROW
BEGIN
SET NEW.navOrder = getNextNavOrder();
END;;

DELIMITER ;

INSERT INTO Table1(item) VALUES('Item1'),('Item2'),('Item3');
INSERT INTO Table2(item) VALUES('Item4'),('Item5'),('Item6');
SELECT * FROM Table1; -- Result 1
SELECT * FROM Table2; -- Result 2
BEGIN;
INSERT INTO Table1(item) VALUES('Item7'),('Item8'),('Item9');
INSERT INTO Table2(item) VALUES('Item10'),('Item11'),('Item12');
SELECT * FROM Table1; -- Result 3
SELECT * FROM Table2; -- Result 4
ROLLBACK;
INSERT INTO Table1(item) VALUES('Item13'),('Item14'),('Item15');
INSERT INTO Table2(item) VALUES('Item16'),('Item17'),('Item18');
SELECT * FROM Table1; -- Result 5
SELECT * FROM Table2; -- Result 6

DROP SCHEMA TestLastInsertId;
_
_Result 1 - Add 3 rows to Table 1 - navOrders 1, 2, and 3.
1   Item1   1   2019-11-02 18:58:28.657690  
2   Item2   2   2019-11-02 18:58:28.657690  
3   Item3   3   2019-11-02 18:58:28.657690  
_
_Result 2 - Add 3 rows to Table 2 - navOrders 4, 5, and 6.
1   Item4   4   2019-11-02 18:58:28.669873  
2   Item5   5   2019-11-02 18:58:28.669873  
3   Item6   6   2019-11-02 18:58:28.669873  
_
_Result 3 - Add 3 more rows to Table 1 - navOrders 7, 8, and 9.
1   Item1   1   2019-11-02 18:58:28.657690  
2   Item2   2   2019-11-02 18:58:28.657690  
3   Item3   3   2019-11-02 18:58:28.657690  
4   Item7   7   2019-11-02 18:58:28.704766  
5   Item8   8   2019-11-02 18:58:28.704766  
6   Item9   9   2019-11-02 18:58:28.704766  
_
_Result 4 - Add 3 more rows to Table 2 - navOrders 10, 11, and 12.
1   Item4   4   2019-11-02 18:58:28.669873  
2   Item5   5   2019-11-02 18:58:28.669873  
3   Item6   6   2019-11-02 18:58:28.669873  
4   Item10  10  2019-11-02 18:58:28.706930  
5   Item11  11  2019-11-02 18:58:28.706930  
6   Item12  12  2019-11-02 18:58:28.706930  
_

ここでロールバックが発生したため、両方のテーブルの行4、5、および6が削除されます。

_Result 5 - Add 3 more rows to Table 1 after a rollback - navOrders 13, 14, and 15.
1   Item1   1   2019-11-02 18:58:28.657690  
2   Item2   2   2019-11-02 18:58:28.657690  
3   Item3   3   2019-11-02 18:58:28.657690  
7   Item13  13  2019-11-02 18:58:28.727303  
8   Item14  14  2019-11-02 18:58:28.727303  
9   Item15  15  2019-11-02 18:58:28.727303  
_
_Result 6 - Add 3 more rows to Table 1 after a rollback - navOrders 16, 17, and 18.
1   Item4   4   2019-11-02 18:58:28.669873  
2   Item5   5   2019-11-02 18:58:28.669873  
3   Item6   6   2019-11-02 18:58:28.669873  
7   Item16  16  2019-11-02 18:58:28.730307  
8   Item17  17  2019-11-02 18:58:28.730307  
9   Item18  18  2019-11-02 18:58:28.730307  
_

NavOrderのUNIQUE制約を削除し、トリガーで呼び出された関数をLAST_INSERT_ID()に置き換えると、重複した値が表示されます。

0