【问题标题】:Loop through columns in MySQL trigger循环遍历 MySQL 触发器中的列
【发布时间】:2014-08-05 08:48:28
【问题描述】:

是否可以在触发器内循环遍历所有列名?

场景: 记录表中所有已修改的列。 如果某些值没有更改,请不要记录这些值。

DROP TRIGGER IF EXISTS t_before_update_test;
DELIMITER $$
CREATE TRIGGER t_before_update_test
BEFORE UPDATE ON test
FOR EACH ROW
BEGIN
    -- Loop here for all columns, not just col1
    IF OLD.col1 <> NEW.col1 THEN
        INSERT INTO change_logs(
            log_on, user_id,
            table_name, colum_name,
            old_data, new_data
        ) VALUES (
            UNIX_TIMESTAMP(NOW()), '0',
            'test', 'col1',
            OLD.col1, NEW.col1
        );
    END IF;
    -- process looping all columns
    -- col1, col2, ... should be dynamic per loop
END $$

这是一个工作副本示例,我现在需要遍历 OLD 或 NEW 中的所有可用列。

【问题讨论】:

    标签: mysql loops triggers calculated-columns


    【解决方案1】:

    是的,可以在触发器中添加光标以循环遍历列。 这里有几个链接:

    mysql, iterate through column names

    https://dba.stackexchange.com/questions/22925/mysql-loop-over-cursor-results-ends-ahead-of-schedule

    根据经验,创建一个执行循环和插入并从触发器调用它的存储过程可能更容易

    【讨论】:

    • 在 Stack Overflow 上不赞成仅链接的答案。最好在这篇文章中包含相关点,以防链接失效。
    • 目前还不清楚如何在触发器内的这些链接中使用解决方案(您需要访问 OLD 和 NEW
    【解决方案2】:

    我现在没有足够的时间来完成这个,但我认为使用CONCAT() 准备一个语句并将其结果用于条件可能会让你做你想做的事。大致如下:

    DECLARE num_rows INT DEFAULT 0;
    DECLARE cols CURSOR FOR SELECT column_name FROM information_schema.columns WHERE table_name = 'table_name' ORDER BY ordinal_position;
    
    OPEN cols; 
    SELECT FOUND_ROWS() INTO num_rows; 
    SET @i = 1;
    
    cols_loop: LOOP
    
        IF @i > num_rows THEN
            CLOSE cols;
            LEAVE cols_loop;
        END IF;
    
        FETCH cols INTO col;
    
        SET @do_stuff = 0;
        SET @s = CONCAT('SELECT IF(NEW.', col, ' <> OLD.', col, ', 1, 0) INTO @do_stuff');
    
        PREPARE stmt1 FROM @s;
        EXECUTE stmt1;
        DEALLOCATE PREPARE stmt1;
    
        IF @do_stuff = 1 THEN
            SET @s2 = CONCAT('INSERT INTO change_logs(log_on, user_id, table_name, colum_name, old_data, new_data ) 
                              VALUES (UNIX_TIMESTAMP(NOW()), ''0'', ''test'', ''', col,''', OLD.', col, ', NEW.', col, ');');
    
            PREPARE stmt2 FROM @s2;
            EXECUTE stmt2;
            DEALLOCATE PREPARE stmt2;
        END IF;
    
        SET @i = @i + 1;  
    END LOOP cols_loop;
    
    CLOSE cols; 
    

    【讨论】:

    【解决方案3】:

    很遗憾,您不能这样做。您可以通过访问 INFORMATION_SCHEMA 获取列名,但无法从该列名访问 OLD 和 NEW 值。我认为这是有道理的,因为与存储过程不同,您正在为特定表而不是为数据库创建触发器。在触发器中调用存储过程将帮助您在一定程度上减少代码。

    DROP TRIGGER IF EXISTS t_before_update_test;
    DELIMITER $$
    CREATE TRIGGER t_before_update_test
    BEFORE UPDATE ON test
    FOR EACH ROW
    BEGIN
        IF OLD.col1 <> NEW.col1 THEN
            /*pseudo*/
            CALL SP_insert_log (  
            'test', 
            'colum_name',
            'old_value',
            ''old_value');
        ELSEIF OLD.col2 <> NEW.col2 THEN
          //call above sp with this column related data
        END IF;
    END $$
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2021-01-29
      • 2010-12-09
      • 2017-10-19
      相关资源
      最近更新 更多