带函数的存储过程使我在Oracle中出错

时间:2018-09-18 13:38:24

标签: function stored-procedures oracle11g

我有存储过程和函数,正在ORACLE中的存储过程中调用该函数。函数 CalculateIncomeTax 是给我错误的原因。在MSSQL中,这种更新是可能的,因为我之前已经做过。我在存储过程中调用了该函数。当我阅读答案时,得到的是在无法使用函数从另一个表更新表之前使用一个包。请告诉我我得到的错误是

  

表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 ;
/

1 个答案:

答案 0 :(得分: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()调用单独的null检查的位置。这些东西与该方法并不直接相关。