Mysql
觸發器內部的動態 SQL 的替代方案?
我的數據庫有以下表格:
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 都將被包裹在
BEGIN
…COMMIT
中以使其成為“原子”。底線:觸發器不能做所有事情。
A 的執行(我認為這將防止其他人繞過儲存的過程。)
CREATE USER special@... ...
– 一個新使用者GRANT INSERT ON db.tbl TO special@...
——讓它進來- 儲存的過程將是
SECURITY special@...
- 將 proc 作為“特殊”執行,因此它可以執行INSERTs
- 從其他人那裡刪除
INSERT PRIVILEGE
該表。(這會變得混亂,或者可能是不必要的。)(如果表位於目前沒有人可以訪問的單獨數據庫中,這可能會有所幫助。)- (我的直覺說還有一步。我會把它作為“讀者練習”。)