【问题标题】:Recursive multiplication sql server递归乘法sql server
【发布时间】:2015-07-01 23:40:15
【问题描述】:

有没有办法将一个数字乘以一个比率,这个乘法的结果乘以另一个比率,然后再将最后一个的结果乘以另一个比率?

乘法是:

100+100*0.1=110

110+110*0.3=143

143+143*0.2=171.6

我有以下表格“收入”和“比率”,我想得到一个查询结果作为“结果”。 我想我应该使用递归查询,但我不知道怎么写 我正在使用 SQL 服务器

with
revenue as (
select '100' revenue 
)
,
ratios as
(
select '1' month_n, '0,1' ratio
union
select '2' month_n, '0,3' ratio
union
select '3' month_n, '0,2' ratio
),
outcome as
(
select '1' month_n, '0,1' ratio, '110' result
union
select '2' month_n, '0,3' ratio, '143' result
union
select '3' month_n, '0,2' ratio, '171,6' result
)

select * from outcome

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?

标签: sql-server recursion sql-server-2012


【解决方案1】:

如果月份不断增加,则可以通过加入month_n - 1来完成递归:

with
revenue as (
select 100 revenue 
)
,
ratios as
(
    select 1 month_n, 0.1 ratio
    union
    select 2 month_n, 0.3 ratio
    union
    select 3 month_n, 0.2 ratio
),
outcome as
(
    select (select MIN(month_n) from ratios) - 1 MONTH_n, 0.0 ratio, cast(revenue as float) result, 1 IsBase from revenue   
    union all
    select r.month_n , r.ratio, (r.ratio + 1.0) * o.result, 0
    from ratios r
    join outcome o on o.MONTH_n = r.month_n - 1
)
select MONTH_n,ratio,result  from outcome where IsBase = 0

结果:

MONTH_n ratio   result
1   0.1 110
2   0.3 143
3   0.2 171,6

但更安全的是使用 row_number 作为索引,尤其是在涉及多年的情况下。


作为一个完全不重要的附注,您也可以在没有多余的“基础”行的情况下编写此代码,如下所示,但这会将计算定义放在两行中。并不是说这会导致额外的计算,只是维护性问题。

....,
outcome as
(
    select top 1 MONTH_n,  ratio, (ratio + 1.0) *  cast(revenue as float) result from revenue , ratios order by month_n
    union all
    select r.month_n , r.ratio, (r.ratio + 1.0) * o.result
    from ratios r
    join outcome o on o.MONTH_n = r.month_n - 1
)
select *  from outcome 

【讨论】:

  • 非常感谢 Me.Name。当我试图用递归解决它时,这就是我正在寻找的答案。但是你也提到了尝试一个超前/滞后函数,这可能会使它更简单,因为真正的查询比我给出的例子复杂得多
【解决方案2】:

如果您的比率不为空,并且始终大于 -1(因为无论如何您都需要加 1 以获得乘法因子)并且您还使用 SQL Server 2012 或更高版本,那么您的查询无需递归即可解决:

WITH Revenue (Revenue) AS (SELECT 100),
Ratios AS (SELECT * FROM (VALUES (1, 0.1), (2, 0.3), (3, 0.2)) t (Month_n, Ratio))

SELECT  ra.month_n,
        ra.Ratio,
        CompoundRatio = EXP(SUM(LOG((1 + ra.Ratio))) OVER(ORDER BY Month_n)),
        Result = rv.Revenue * EXP(SUM(LOG((1 + ra.Ratio))) OVER(ORDER BY Month_n))
FROM    Ratios AS ra
        CROSS JOIN Revenue AS rv;

这给出了:

month_n Ratio   Compound    Result
-------------------------------------
1       0.1     1.1         110
2       0.3     1.43        143
3       0.2     1.716       171.6

您可以使用窗口函数来总结以前的比率,以获得复合比率,但由于 SQL Server 没有产品聚合,您需要使用 LOGEXP 来获得以前比率的产品.

如果您的比率可以是负数和/或 0,那么您仍然可以这样做,但您的逻辑会变得有点复杂。请参阅 this answer 以了解有关检查功能的更多说明:

WITH Revenue (Revenue) AS (SELECT 100),
Ratios AS (SELECT * FROM (VALUES (1, 0.1), (2, 0.3), (3, 0.2), (4, -0.1), (5, 0.2), (6, -0.2), (7, 0.05), (8, -1.1)) t (Month_n, Ratio))

SELECT  ra.month_n,
        ra.Ratio,
        CompoundRatio = CASE WHEN MIN(ABS(ra.Ratio + 1)) OVER(ORDER BY ra.Month_n) = 0 THEN 0
                            ELSE CASE WHEN SUM(CASE WHEN ra.Ratio < -1 THEN 1 ELSE 0 END) 
                                            OVER(ORDER BY ra.Month_n) % 2 = 1 THEN -1 ELSE 1 END *
                                EXP(SUM(LOG(ABS(NULLIF(1 + ra.Ratio, 0)))) OVER(ORDER BY Month_n))
                        END,
        Result = CASE WHEN MIN(ABS(ra.Ratio + 1)) OVER(ORDER BY ra.Month_n) = 0 THEN 0
                    ELSE CASE WHEN SUM(CASE WHEN ra.Ratio < -1 THEN 1 ELSE 0 END) 
                                    OVER(ORDER BY ra.Month_n) % 2 = 1 THEN -1 ELSE 1 END *
                        EXP(SUM(LOG(ABS(NULLIF(1 + ra.Ratio, 0)))) OVER(ORDER BY Month_n))
                END * rv.Revenue
FROM    Ratios AS ra
        CROSS JOIN Revenue AS rv;

结果(经过完整性检查):

month_n Ratio   CompoundRatio   SQLResult       Equation
------------------------------------------------------------------------
1       0.1     1.1             110             100 + 100 x 0.1 = 110
2       0.3     1.43            143             110 + 110 x 0.3 = 143
3       0.2     1.716           171.6           143 + 143 x 0.2 = 171.6
4       -0.1    1.5444          154.44          171.6 + 171.6 x -0.1 = 154.44
5       0.2     1.85328         185.328         154.44 + 154.44 x 0.2 = 185.328
6       -0.2    1.482624        148.2624        185.328 + 185.328 x -0.2 = 148.2624
7       0.05    1.5567552       155.67552       148.2624 + 148.2624 x 0.05 = 155.67552
8       -1.1    -0.15567552     -15.567552      155.67552 + 155.67552 x -1.1 = -15.567552

【讨论】:

  • 谢谢 GarethD。我使用的是 SQL Server 2012,但我确实有很多负比率。我想通过使用 EXP 它不会在负比率下工作
  • 你的负值是否小于-1?无论如何,每个比率都添加了 1 - 重写您的第一个方程式 100 + 100 * 0.1 等效于 100 * (1 + 0.1),因此即使您的比率是 -0.9,这对于当前方法来说也不是问题。如果您确实有低于此值的值,那么我将添加一个更新的公式来解决此问题。
  • 不,不,它们不能小于 -1。好吧,我也会尝试你的第一个解决方案
  • 感谢 GarethD,我花了一些时间来适应真正的查询,但效果很好
猜你喜欢
  • 2022-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-19
  • 2011-01-10
相关资源
最近更新 更多