【问题标题】:Stored Procedure With Function giving me errors in Oracle带有函数的存储过程在 Oracle 中给了我错误
【发布时间】:2019-02-22 13:38:28
【问题描述】:

我有存储过程和函数,我在 ORACLE 的存储过程中调用函数。函数 CalculateIncomeTax 是给我错误的原因。在 MSSQL 中,这种类型的更新是可能的,因为我以前做过。我在存储过程中调用了该函数。当我阅读答案时,我得到的答案是在我不能使用函数从另一个表更新表之前使用包。如果你有任何想法,请告诉我.我得到的错误是

table string.string 正在变异,触发器/函数可能看不到它 原因:触发器(或此语句中引用的用户定义的 plsql 函数)试图查看(或修改)正在被触发它的语句修改的表。 行动:重写触发器(或函数),使其不读取该表。

这是函数

CREATE OR REPLACE function CalculateIncomeTax(periodId NVARCHAR2, 
employeeId NVARCHAR2, taxableIncome NUMBER)return NUMBER 
AS
IncomeTax NUMBER (18,4);Taxable NUMBER(18,4);            
BEGIN
SELECT SUM(CASE WHEN (taxableIncome > T.TAX_CUMMULATIVE_AMOUNT)
THEN (taxableIncome -  T.TAX_CUMMULATIVE_AMOUNT)* T.TAX_PERCENTAGE/ 100                                                        
ELSE 0.00 END )  INTO IncomeTax  
FROM TAX_LAW T  JOIN PAY_GROUP P ON P.PAY_FORMULA_ID   =T.TAX_FORMULA_ID              
JOIN PAYROLL_MASTER PP ON P.PAY_CODE =PP.PAY_PAY_GROUP_CODE        
WHERE PP.PAY_EMPLOYEE_ID = employeeId AND PP.PAY_PERIOD_CODE = periodId;         
if IncomeTax IS NULL THEN  IncomeTax :=0;     
end if;      
return IncomeTax;
end;/

这是存储过程

CREATE OR REPLACE PROCEDURE PROCESSPAYROLLMASTER (periodcode 
VARCHAR2) AS BEGIN         
INSERT INTO  PAYROLL_MASTER 
(       
PAY_PAYROLL_ID,PAY_EMPLOYEE_ID ,PAY_EMPLOYEE_NAME,PAY_SALARY_GRADE_CODE        
,PAY_SALARY_NOTCH_CODE,PAY_BASIC_SALARY,PAY_TOTAL_ALLOWANCE
,PAY_TOTAL_CASH_BENEFIT,PAY_MEDICAL_BENEFIT,PAY_TOTAL_BENEFIT
,PAY_TOTAL_DEDUCTION,PAY_GROSS_SALARY,PAY_TOTAL_TAXABLE,PAY_INCOME_TAX
,PAY_TAXABLE,PAY_PERIOD_CODE,PAY_BANK_CODE,PAY_BANK_NAME,PAY_BANK_ACCOUNT_NO    
,PAY_PAY_GROUP_CODE )
SELECT 
1, 
E.EMP_ID AS PAY_EMPLOYEE_ID ,
E.EMP_FIRST_NAME || ' ' || E.EMP_LAST_NAME  AS PAY_EMPLOYEE_NAME,
E.EMP_RANK_CODE,
'CODE',
(SC.SAL_MINIMUM_AMOUNT+( SN.SAL_SALARY_PERCENTAGE * 
SC.SAL_MINIMUM_AMOUNT)/100) AS PAY_BASIC_SALARY,
0,
0,
0,
0,
0,
0,
0,
0,
0,
periodcode,
'BANKCODE',
'BANKNAME',
'BANKNUMBER',
'GENERAL'
FROM EMPLOYEE E 
LEFT JOIN SALARY_SCALE  SC ON SC.SAL_RANK_CODE = E.EMP_RANK_CODE
LEFT JOIN SALARY_NOTCH SN ON  SC.SAL_ID = SN.SAL_SALARYSCALE_ID
WHERE E.EMP_RANK_CODE = SC.SAL_RANK_CODE AND E.EMP_STATUS=2;

CALCULATEALLOWANCE(v_payrollId,periodcode);
CALCULATECASHBENEFITS(v_payrollId,periodcode);
CALCULATEDEDUCTIONS(v_payrollId,periodcode);

-- UPDATE PAYROLL PAY_INCOME_TAX
   UPDATE PAYROLL_MASTER PM  SET  PM.PAY_INCOME_TAX = CalculateIncomeTax(PM.PAY_PERIOD_CODE,PM.PAY_EMPLOYEE_ID,PM.PAY_TOTAL_TAXABLE) WHERE PM.PAY_PAYROLL_ID = v_payrollId;

 UPDATE PAYROLL_PROCESS set PAY_CANCELLED = 1 WHERE PAY_PAY_GROUP_CODE='GENERAL' AND PAY_PERIOD_CODE=periodcode
 AND PAY_ID<>v_payrollId;


COMMIT;  
END ;
/

【问题讨论】:

  • 我在两张表上没有任何触发器,只有表ID是主键
  • CREATE OR REPLACE TRIGGER TR_PAYROLL_MASTER before insert on "SIBAHR"."PAYROLL_MASTER" 为每一行开始选择 "SIBAHR"."SQ_PAYROLL_MASTER".nextval 到 :new."PAY_ID" from dual;结尾; /
  • 我正在使用该函数计算所得税,它需要存储过程中已经定义的三个参数。之后它将更新数据库中的指定列。
  • 你不需要触发器,一个函数可能会导致错误。您正在使用查询该表的函数调用更新payroll_master 表。最简单的方法是重写insert...select 以包含其他表并一次计算所有字段值,完全避免使用该函数。
  • 同样的查询在 MSSQL Server 中运行没有任何问题,为什么 Oracle 给我一个错误。我正在从另一个表中挑选数据,使用这些值进行计算。老实说,我不知道该怎么做你的建议

标签: function stored-procedures oracle11g


【解决方案1】:

该函数正在查询您正在更新的同一个表,这就是错误报告的内容。碰巧的是,您并没有更改要查询的列的值,但 Oracle 不会检查到该级别 - 尤其是因为可能存在例如副作用不太明显的触发器。

最好的解决方案实际上是根本不必更新,并通过加入所有相关表来计算和设置所有值作为原始插入的一部分。但是您已经在调用其他程序,这些程序可能会更新您插入为零的一些值,包括pay_total_taxable

除非您也能够重新评估这些内容,否则您可能无法进行进一步的更新。在这种情况下,您可以从函数中删除对payroll_master 表的引用,并传入相关数据。

我认为这是等价的,尽管没有表结构、示例数据和其他程序在做什么很难确定(因此显然这是未经测试的):

create or replace function calculateincometax (
  p_periodid nvarchar2,
  p_employeeid nvarchar2,
  p_paypaygroupcode payroll_master.pay_pay_group_code%type,
  p_taxableincome number
) return number as
  l_incometax number(18, 4);
begin
  select coalesce(sum(case when p_taxableincome > t.tax_cummulative_amount
    then (taxableincome - t.tax_cummulative_amount) * t.tax_percentage / 100
    else 0 end), 0)
  into l_incometax
  from tax_law t
  join pay_group p
  on p.pay_formula_id = t.tax_formula_id
  where p.pay_code = p_paypaygroupcode;

  return l_incometax;
end;
/

然后在你的调用中包含额外的参数:

  update payroll_master pm
  set pm.pay_income_tax = calculateincometax(pm.pay_period_code, pm.pay_employee_id,
      pm.pay_pay_group_code, pm.pay_total_taxable)
  where pm.pay_payroll_id = v_payrollid;

虽然v_payrollid 没有在您显示的内容中定义,所以即使这样也不是很清楚。

我还修改了带有前缀的函数参数和局部变量名称以消除潜在的歧义(您似乎可以通过从名称中删除下划线来做到这一点),删除未使用的变量,并添加了一个 coalesce() 调用来代替单独的空检查。不过,这些事情与方法没有直接关系。

【讨论】:

  • 其他程序用于从其他表中获取值,我为一个人将这些值相加并用它来更新 pay_master,我遇到的唯一问题是函数,我会试试你的建议看看。我也从我的表中删除了触发器。
  • @lutakyn - 您在评论中显示的插入前触发器不会导致更新问题。
  • 感谢兄弟,我会进一步研究代码。
  • 我怎样才能得到你的电子邮件地址?
猜你喜欢
  • 1970-01-01
  • 2015-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-29
  • 2013-11-17
  • 2013-11-02
  • 2022-01-13
相关资源
最近更新 更多