Mysql

觸發器內部的動態 SQL 的替代方案?

  • July 24, 2018

我的數據庫有以下表格:

   TAG
   ----------------------
   | tag_id | tag_name  |
   ----------------------

   TAG_VALUE: Stores values associated to each tag
   ----------------------------------------
   | tag_id | insertion_timestamp | value |
   ----------------------------------------

   ALARM: Defines alarms for each tag
   -------------------------------------
   | alarm_id | tag_id | function_name |
   -------------------------------------

   ALARM ACTIVATION: Stores information regarding each time the alarms were triggered
   -----------------------------------------------------
   | alarm_id | activation_timestamp | activation_value|
   -----------------------------------------------------

因此,每次將新值插入 TAG_VALUE 時,我都需要檢查該新值是否觸發與其標籤關聯的任何警報。由於警報之間沒有共同的標準,我只是儲存了一個函式名稱,稍後將使用它來確定是否應該觸發警報

TAG_VALUE 具有以下 AFTER_INSERT 觸發器:

CREATE DEFINER=`root`@`localhost` TRIGGER `mydb`.`tag_value_AFTER_INSERT` AFTER INSERT ON `tag_value` FOR EACH ROW
BEGIN
call sp_alarm_handler(NEW.tag_value, NEW.tag_id);
END

而 sp_alarm_handler 的編碼如下:

CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_alarm_handler`(IN tag_value VARCHAR(255), IN value_tag_id INT(11))
   BEGIN

   DECLARE exit_loop BOOLEAN;
   DECLARE v_alarm_id INT(11);
   DECLARE function_name VARCHAR(255);
   DECLARE value_triggers_alarm TINYINT(1);

   DECLARE custom_alarm_cur CURSOR FOR
   Select alarm_id, function_name From vw_custom_alarms where tag_id = value_tag_id;


   DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;

   OPEN custom_alarm_cur;

   custom_alarm_lp: LOOP

       FETCH custom_alarm_cur into v_alarm_id, function_name;

       IF exit_loop THEN
       leave custom_alarm_lp;
       END IF;

       #************************************************
       #*********HERE'S THE DYNAMIC SQL PIECE'**********
       #************************************************
       set @query = CONCAT('Select ',function_name,'(',tag_value,')', 'into @value_triggers_alarm');
       PREPARE stmt FROM @query;
       Execute stmt;

       IF fn_is_alarm_active(v_alarm_id) = 0 THEN

           If @value_triggers_alarm = 1 THEN
                   INSERT INTO alarm_activation(alarm_id, activation_timestamp, activation_value)
                   VALUES (v_alarm_id, NOW(), tag_value);
           end if;

       ELSE IF @value_triggers_alarm = 0 THEN
           call sp_deactivate_alarm(v_alarm_id);

           END IF;
       END IF;



   END LOOP custom_alarm_lp;
   Close custom_alarm_cur;

   END

這給了我一個ERROR 1336: 1336: Dynamic SQL is not allowed in stored function or trigger 在我的情況下有什麼解決方法嗎?

如果要檢查的列表是不變的,那麼將它們拼寫出來而不是建構測試。(不可行)

但是,估計不是這樣的……

方案 A:將INSERT需要呼叫觸發器的替換為PROCEDURE呼叫。該常式包含INSERT您提供的其餘程式碼。

計劃 B:與計劃 A 一樣,但在應用程式碼中執行。

注意:A 或 B 都將被包裹在BEGINCOMMIT中以使其成為“原子”。

底線:觸發器不能做所有事情。

A 的執行(我認為這將防止其他人繞過儲存的過程。)

  • CREATE USER special@... ... – 一個新使用者
  • GRANT INSERT ON db.tbl TO special@... ——讓它進來
  • 儲存的過程將是SECURITY special@... - 將 proc 作為“特殊”執行,因此它可以執行INSERTs
  • 從其他人那裡刪除INSERT PRIVILEGE該表。(這會變得混亂,或者可能是不必要的。)(如果表位於目前沒有人可以訪問的單獨數據庫中,這可能會有所幫助。)
  • (我的直覺說還有一步。我會把它作為“讀者練習”。)

引用自:https://dba.stackexchange.com/questions/172644