返回

MySQL函数数据插入:陷阱、解决与安全

mysql

MySQL函数插入数据:问题分析与解决

函数内数据插入的常见陷阱

尝试使用 MySQL 函数来向表中插入数据是一种常见的需求。然而,在实际操作中,开发者可能会遇到一些意料之外的行为。当函数直接被调用时,例如select fn_eval ('1', 2, '3') from dual;,它可能可以正常运行,但当在查询中以 inline 方式使用时,则不会插入数据。 这可能让人感到困惑。问题的关键在于理解 MySQL 函数的一些限制及其与数据库交互的方式。

MySQL 函数的定义中使用了 deterministic ,这通常表明对于给定的输入,函数总是返回相同的结果。 理想状态下,这能够为查询优化器提供帮助。但请注意,deterministic 的定义并未保证在函数执行期间修改数据库。实际情况是,即使一个函数标明了确定性,它也允许包含数据库写入操作。但数据库的优化机制,却不会真的执行。 数据库本身是为了保证数据一致性。

解决办法一:使用存储过程代替函数

一个简单的解决思路是使用存储过程而非函数。存储过程可以执行诸如插入、更新等数据操作,且能被正常调用执行。

以下是将上面的函数改写为存储过程的示例:

DELIMITER //

CREATE PROCEDURE sp_eval (
    IN a VARCHAR(36),
    IN b INT,
    IN c VARCHAR(36)
)
BEGIN
    INSERT INTO eval_tbl (a, b, c, d)
    SELECT a, b, c, d
    FROM   (
           (SELECT a,b,c,d FROM eval_tbl_1
              INNER JOIN eval_tbl_2 ON eval_tbl_1.c = eval_tbl_2.c)
    );
END //

DELIMITER ;

使用存储过程调用数据:

CALL sp_eval('1', 2, '3');

步骤:

  1. 使用 DELIMITER // 将默认分隔符设置为 // ,方便定义存储过程。
  2. CREATE PROCEDURE 创建名为 sp_eval 的存储过程,输入参数为 abc
  3. 过程体内的 INSERT INTO 语句负责实际的数据插入操作。
  4. DELIMITER ; 恢复默认分隔符。
  5. 使用 CALL 语句执行存储过程,传入相应的参数。

这种方法清晰直接,并且存储过程天然就适合执行类似数据变更的操作。

解决办法二:触发器配合函数(谨慎使用)

虽然推荐使用存储过程,但是如果特别需要使用函数,且要达到修改表的效果。 可以考虑使用触发器配合函数。 触发器在特定事件发生时,会自动触发执行一系列 SQL 语句,因此可以配合一个简单函数来实现数据插入。 需要 谨慎考虑触发器可能带来的性能和数据一致性问题

例如: 当插入eval_tbl_1 时,触发器自动执行函数:

delimiter //
CREATE FUNCTION fn_eval_trigger
  (
    a VARCHAR(36),
    b INT,
    c VARCHAR(36)
   )
RETURNS BIGINT
DETERMINISTIC
BEGIN
    INSERT INTO eval_tbl (a, b, c, d)
    SELECT a, b, c, d
      FROM (
           SELECT a,b,c,d FROM eval_tbl_1
              INNER JOIN eval_tbl_2 ON eval_tbl_1.c = eval_tbl_2.c
          );
    RETURN 1;
END;

//
CREATE TRIGGER eval_trigger
AFTER INSERT
ON eval_tbl_1
FOR EACH ROW
BEGIN
   SELECT fn_eval_trigger(NEW.a, NEW.b, NEW.c);
END;
//

delimiter ;

步骤:

  1. 创建一个 fn_eval_trigger 函数, 函数内容主要处理数据的插入, 并返回一个值(实际意义不大)。
  2. 创建一个名为 eval_trigger 的触发器。AFTER INSERT 指定触发时机是在数据插入到 eval_tbl_1 之后。FOR EACH ROW 表示每插入一行数据都会执行触发器里的代码。
  3. 在触发器体内,调用fn_eval_trigger函数, 并传入新插入行的数据。NEW 表示当前插入行的变量值。
  4. 现在向eval_tbl_1中插入数据,会触发相应的数据插入操作。

需要 强调 的是:触发器应避免过度使用。它们可能影响数据库性能,并在不显眼的位置隐藏数据变更逻辑。维护复杂的触发器也增加了数据库的管理难度,同时排错也会比较复杂。 务必评估使用场景的必要性。

安全注意事项

无论选择存储过程还是触发器,都需要注意一些安全事项。

  1. 参数校验 : 在数据写入前对输入参数进行有效性检查,防止SQL 注入或者无效数据写入。
  2. 最小权限原则 : 授予用户执行存储过程或者触发器的最小权限。不要赋予过多权限。
  3. 日志记录 : 在存储过程或者触发器中加入必要的日志记录,便于追溯和分析数据变更行为。
  4. 监控 : 密切关注存储过程和触发器的执行状态,发现性能问题或错误及时处理。

总结来说, 使用存储过程是相对更安全、直接的方法。 当确有必要时才考虑触发器。 在使用它们执行数据库修改时务必保持谨慎。