【问题标题】:MySQL user-defined function returns incorrect value when used in a SELECT statementMySQL 用户定义函数在 SELECT 语句中使用时返回不正确的值
【发布时间】:2016-10-21 01:21:01
【问题描述】:

在 MySQL 中调用用户定义的函数时遇到问题。计算非常简单,但无法掌握出错的地方以及出错的原因。事情是这样的。

所以我创建了这个函数:

DELIMITER //
CREATE FUNCTION fn_computeLoanAmortization (_empId INT, _typeId INT)
RETURNS DECIMAL(17, 2) 

BEGIN
    SET @loanDeduction = 0.00;

    SELECT TotalAmount, PeriodicDeduction, TotalInstallments, DeductionFlag
    INTO @totalAmount, @periodicDeduction, @totalInstallments, @deductionFlag
    FROM loans_table
    WHERE TypeId = _typeId AND EmpId = _empId;

    IF (@deductionFlag = 1) THEN
        SET @remaining = @totalAmount - @totalInstallments;

        IF(@remaining < @periodicDeduction) THEN
            SET @loanDeduction = @remaining;
        ELSE
            SET @loanDeduction = @periodicDeduction;
        END IF;
    END IF;

    RETURN @loanDeduction;
END;//
DELIMITER ;

如果我这样称呼它,它可以正常工作:

SELECT fn_computeLoanAmortization(3, 4)

但是如果我在 SELECT 语句中调用它,结果就会出错:

SELECT Id, fn_computeLoanAmortization(Id, 4) AS Amort FROM emp_table

loans_table 中只有一个条目,上面的语句应该只导致 Amort 列中有一行具有值,但是有很多随机行具有相同的值Amort 值作为匹配条目的值,不应该是这种情况。

有没有人遇到过这种奇怪的困境?或者我可能做错了什么。请赐教。

非常感谢。

编辑: 错误的意思是这样的:

loans_table has one record
EmpId = 1
TypeId = 2
PeriodicDeduction = 100
TotalAmount = 1000
TotalInstallments = 200
DeductionFlag = 1

emp_table has several rows 
EmpId = 1
Name = Paolo

EmpId = 2
Name = Nikko

...

EmpId = 5
Name = Ariel

当我查询以下语句时,我得到了正确的值:

SELECT fn_computeLoanAmortization(1, 2)

SELECT Id, fn_computeLoanAmortization(Id, 2) AS Amort FROM emp_table WHERE EmpId = 1

但是当我查询这个语句时,我得到了不正确的值:

SELECT Id, fn_computeLoanAmortization(Id, 2) AS Amort FROM emp_table

结果集将是:

EmpId    |    Amort
--------------------
1        |    100
2        |    100 (this should be 0, but the query returns 100)
3        |    100 (same error here)
...
5        |    100 (same error here up to the last record)

【问题讨论】:

  • 请举例说明“错误”的含义。
  • 您没有向我们展示真正的查询。在问题中,您将 my_table 写为表名。我想,你真正的查询有一个连接或者在不同的表上执行而不是loans_表
  • 如何运行:SELECT Id, fn_computeLoanAmortization(Id, 4) AS Amort FROM my_table WHERE Id = 3; ?
  • @NineBerry - 这是一张员工表。它只是一张基本表,没有连接或诸如此类的东西。它没有被编辑。谢谢。
  • @arkki - 当我挑选出 Id 时,我得到了正确的值,但是当我在组中使用它时(比如在查询中使用 IN),它显示不正确的数据..(例如值应该是0,因为它在 loan_table 中没有匹配项,但它的值是 1000,它不知从何而来)

标签: mysql function user-defined-functions


【解决方案1】:

在您的函数内部,您用来从loans_table 表中检索值的变量不是函数本地的局部变量,而是会话变量。当函数内部的 select 没有找到任何行时,这些变量的值仍然与之前执行函数时的值相同。

改用真正的局部变量。为此,请使用不带 @ 作为前缀的变量名称,并在函数开头声明变量。详情请见this answer

【讨论】:

  • 谢谢。会试试这个,之后会提供反馈。
  • 这对我有用。这对我来说也是一个新的学习。非常感谢。
【解决方案2】:

我怀疑问题是INTO中的变量在没有匹配行时没有重新设置。

只需将它们设置在之前 INTO:

BEGIN
    SET @loanDeduction = 0.00;
    SET @totalAmount = 0;
    SET @periodicDeduction = 0;
    SET @totalInstallments = 0;
    SET @deductionFlag = 0;

    SELECT TotalAmount, PeriodicDeduction, TotalInstallments, DeductionFlag
    . . . 

您可能只想将它们设置为NULL

或者,切换您的逻辑以使用局部变量:

    SET v_loanDeduction = 0.00;
    SET v_totalAmount = 0;
    SET v_periodicDeduction = 0;
    SET v_totalInstallments = 0;
    SET v_deductionFlag = 0;

等等。

【讨论】:

  • 这也将解决这个特定的问题,但是使用会话变量而不是局部变量在多个函数中使用同名或并行执行语句时可能会产生其他意想不到的副作用。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 2023-03-09
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
  • 2016-12-06
  • 1970-01-01
相关资源
最近更新 更多