3

我正在尝试使用以下代码创建一个 MySQL Before Insert 触发器,如果​​我能找到一种方法来执行由触发器生成的准备好的语句,它将执行我希望它执行的操作。

是否有任何替代方法可以从触发器中执行准备好的语句?谢谢

BEGIN
    SET @CrntRcrd = (SELECT AUTO_INCREMENT FROM information_schema.TABLES 
              WHERE TABLE_SCHEMA=DATABASE()
              AND TABLE_NAME='core_Test');

  SET @PrevRcrd = @CrntRcrd-1;

    IF (NEW.ID IS NULL) THEN
        SET NEW.ID = @CrntRcrd;
    END IF;

    SET @PrevHash = (SELECT Hash FROM core_Test WHERE Record=@PrevRcrd);

    SET @ClmNms = (SELECT CONCAT('NEW.',GROUP_CONCAT(column_name 
                  ORDER BY ORDINAL_POSITION SEPARATOR ',NEW.'),'')
                  FROM information_schema.columns 
                  WHERE table_schema = DATABASE() 
                  AND table_name = 'core_Test');

  SET @Query = CONCAT("SET @Query2 = CONCAT_WS(',','",@PrevHash,"','", @CrntRcrd, "',", @ClmNms, ");");

  PREPARE stmt1 FROM @Query;
  EXECUTE stmt1;
  DEALLOCATE PREPARE stmt1; 

  SET NEW.Hash = @Query2;
END

更新/澄清:数据将存储在下表中。

+------------+-----+------+----------------+
| Record (AI)| ID  | Data | HASH           |
+------------+-----+------+----------------+
| 1          | 1   | ASDF | =DHFBGKJSDFHBG | (Hash Col 1)
| 2          | 2   | NULL | =UEGFRYJKSDFHB | (Hash Col 1 + Col 2)
| 3          | 1   | VBNM | =VKJSZDFVHBFJH | (Hash Col 2 + Col 3)
| 4          | 4   | TYUI | =KDJFGNJBHMNVB | (Hash Col 3 + Col 4)
| 5          | 5   | ZXCV | =SDKVBCVJHBJHB | (Hash Col 4 + Col 5)
+------------+-----+------+----------------+

在每个插入命令上,该表将通过将前一行的哈希值应用到整个新行的 CONCAT() 中,然后重新对整个字符串进行哈希处理,从而为该行生成一个哈希值。这将创建哈希值的运行记录,用于审计目的/在应用程序的另一部分中使用。

我的限制是这必须在 INSERT 之前完成,因为之后无法更新行。

更新:我目前正在使用以下代码,直到找到一种将列名动态传递给 CONCAT 的方法:

BEGIN

  SET @Record = (
    SELECT AUTO_INCREMENT FROM information_schema.TABLES 
    WHERE TABLE_SCHEMA=DATABASE()
    AND TABLE_NAME='core_Test'    #<--- UPDATE TABLE_NAME HERE
  );
  SET @PrevRecrd = @Record-1;

    IF (new.ID IS NULL) THEN
    SET new.ID = @Record;
  END IF;

  SET @PrevHash = (
    SELECT Hash FROM core_Test    #<--- UPDATE TABLE_NAME HERE
    WHERE Record=@PrevRecrd
  );

  SET new.Hash = SHA1(CONCAT_WS(',',@PrevHash, @Record,
    /* --- UPDATE TABLE COLUMN NAMES HERE (EXCLUDE "new.Record" AND "new.Hash") --- */
    new.ID, new.Name, new.Data
  ));

END
4

1 回答 1

2

简短的回答是您不能在 TRIGGER 中使用动态 SQL。

我对 auto_increment 值的查询感到困惑,并为 ID 列分配了一个值。我不明白为什么需要设置 ID 列的值。那不是定义为AUTO_INCREMENT的列吗?数据库将处理分配。

也不清楚您的查询是否保证返回唯一值,尤其是在运行并发插入时。(我没有测试过,所以它可能有效。)

但是代码很奇特。

看起来您想要完成的是从最近插入的行中获取列的值。我认为查询定义触发器的同一个表有一些限制。(我知道甲骨文肯定有;MySQL 可能更自由。)

如果我需要做类似的事情,我会尝试这样的事情:

 SELECT @prev_hash := t.hash AS prev_hash 
   FROM core_Test t
  ORDER BY t.ID DESC LIMIT 1;

 SET NEW.hash = @prev_hash; 

但同样,我不确定这会奏效(我需要测试)。如果它适用于一个简单的案例,那并不能证明它一直有效,在并发插入的情况下,在扩展插入的情况下,等等。

我按照我的方式编写查询,以便它可以利用 ID 列上的索引来执行反向扫描操作。如果它不使用索引,我会尝试重写该查询(可能作为 JOIN,以获得最佳性能。

 SELECT @prev_hash := t.hash AS prev_hash
   FROM ( SELECT r.ID FROM core_Test r ORDER BY r.ID DESC LIMIT 1 ) s
   JOIN core_Test t
     ON t.ID = s.ID

摘自 MySQL 5.1 参考手册
E.1 对存储程序的限制

SQL 准备语句(PREPARE、EXECUTE、DEALLOCATE PREPARE)可用于存储过程,但不能用于存储函数或 触发器。因此,存储函数和触发器不能使用动态 SQL(您将语句构造为字符串然后执行它们)。 [原文如此]

于 2012-07-17T02:58:54.030 回答