在Oracle中正确使用sum和case作为循环

在Oracle中正确使用sum和case作为循环,oracle,function,plsql,Oracle,Function,Plsql,我在一个表格中循环计算IncomeTax,将应纳税作为一个参数。因此,循环遍历表格,计算并求和这些值。在sum语句中,应纳税小于累计金额,然后我们乘以应纳税*百分比。该示例解释了我需要的函数。例如 taxable=2000, do 2000-261=1739 and do 0% * 261 = 0, go to next line do 1739-70=1669 and do 5% * 70=3.5, go to next line do 1669-100=1569 and do 10% *10

我在一个表格中循环计算IncomeTax,将应纳税作为一个参数。因此,循环遍历表格,计算并求和这些值。在sum语句中,应纳税小于累计金额,然后我们乘以应纳税*百分比。该示例解释了我需要的函数。例如

taxable=2000,
do 2000-261=1739 and do 0% * 261 = 0,
go to next line do 1739-70=1669 and do 5% * 70=3.5,
go to next line do 1669-100=1569 and do 10% *100.
go to next line,do 1569 < 2810, it should be 1569 *17.5%

因此,您正在编写一个PL/SQL函数,但试图用一个简单的SQL查询完成所有逻辑。我认为这当然可以通过SQL查询实现,但由于原始逻辑是以循环的形式构造的,因此使用PL/SQL循环结构可能会更容易

CREATE OR REPLACE function CalculateIncomeTax2(taxableIncome NUMBER)
return NUMBER 
AS
    IncomeTax NUMBER (10,2) := 0;
    TaxableRemainder NUMBER := taxableIncome;
BEGIN
    for r in (select tax_id, tax_percentage, tax_cummulative_amount
              from incometax
              order by tax_id)
    loop
        if TaxableRemainder > r.tax_cummulative_amount then
            TaxableRemainder := TaxableRemainder - r.tax_cummulative_amount;
            incometax := incometax + (r.tax_cummulative_amount * r.tax_percentage / 100);
        else
            incometax := incometax + (TaxableRemainder * r.tax_percentage / 100);
            exit; 
        end if;
    end loop;

    return IncomeTax;
end; 
/
例如,我得到了
CalculateIncomeTax2(2000)=288.08
。这与通常使用计算器(288.075)得到的结果略有不同,因为您将IncomeTax定义为精度为2的数字(10,2)。但也许这就是你想要的

编辑:因此,在SQL查询中,您缺少的主要内容是为了保持每行的税值的运行总数,您需要一个。下面是一个示例,说明如何在普通SQL查询中执行相同的计算。(您可以大大简化此查询,但我认为这样更具可读性—您可以轻松查看子查询以了解数据是如何构建的。)


4000减261不是1739。你的意思是从应税=2000开始吗?我写的查询在sql中可以很好地计算IncomeTax,但不是Plsql,我会尝试你的逻辑,尽管我不是100%理解它。谢谢你,我会回复你。我有没有办法私下联系你?我在sql中写的和你做的有什么区别?
CREATE OR REPLACE function CalculateIncomeTax2(taxableIncome NUMBER)
return NUMBER 
AS
    IncomeTax NUMBER (10,2) := 0;
    TaxableRemainder NUMBER := taxableIncome;
BEGIN
    for r in (select tax_id, tax_percentage, tax_cummulative_amount
              from incometax
              order by tax_id)
    loop
        if TaxableRemainder > r.tax_cummulative_amount then
            TaxableRemainder := TaxableRemainder - r.tax_cummulative_amount;
            incometax := incometax + (r.tax_cummulative_amount * r.tax_percentage / 100);
        else
            incometax := incometax + (TaxableRemainder * r.tax_percentage / 100);
            exit; 
        end if;
    end loop;

    return IncomeTax;
end; 
/
select sum(tax_component)
from (
    select tax_id, tax_percentage, tax_cummulative_amount,
        taxableRemainder,
        case when taxableRemainder > tax_cummulative_amount 
            then (tax_cummulative_amount * tax_percentage / 100)
            else (taxableRemainder * tax_percentage / 100)
            end as tax_component
    from (
        select tax_id, tax_percentage, tax_cummulative_amount,
            nvl(2000-sum(tax_cummulative_amount) 
                     over (order by tax_id 
                        rows between unbounded preceding and 1 preceding),
                2000) 
                as taxableRemainder
        from incometax) it
)
;