计算当前行列值和上一行计算列值的乘积

时间:2016-05-05 04:58:05

标签: sql

下面是我需要使用SQL

执行的数据集和计算类型
ID   Postdate   Return   LinkedValue
------------------------------
1001 7/1/2013   100      100
1001 7/2/2013   101      101
1001 7/3/2013   102      102*101/100
1001 7/4/2013   103      103*102*100/100

第一行的LinkedValue将等于返回值,但对于之后的每个连续行,它将是当前行的返回和为前一行计算的LinkedValue的乘积。

SQL中有没有办法实现这一目标?

2 个答案:

答案 0 :(得分:0)

我认为这就是你要找的东西:

WITH    cte_prep
          AS (
               SELECT
                    [rowid]
                ,   [ID]
                ,   [Postdate]
                ,   [Return]
                ,   RANK() OVER ( PARTITION BY ID ORDER BY Postdate ) AS P_Rank
                FROM
                    [meta].[dbo].[return]
             ),
        cte_recursive ( ID, Postdate, [Return], P_Rank, preturn, string, Level )
          AS (
               -- Anchor member definition
    SELECT
            e.ID
        ,   e.Postdate
        ,   e.[Return]
        ,   e.P_Rank
        ,   10 AS preturn
        ,   CAST(e.[return] AS NVARCHAR(MAX)) AS string
        ,   0 AS Level
        FROM
            cte_prep AS e
        WHERE
            e.P_Rank = 1
               UNION ALL
-- Recursive member definition
               SELECT
                    e.ID
                ,   e.Postdate
                ,   e.[Return]
                ,   e.P_Rank
                ,   d.[return] AS preturn
                ,   CAST(d.string + ' * ' + CAST(e.[return] AS NVARCHAR(50)) AS NVARCHAR(MAX)) AS string
                ,   Level + 1 AS Level
                FROM
                    cte_prep AS e
                INNER JOIN cte_recursive AS d
                ON  e.ID = d.ID AND
                    e.P_rank = d.P_rank + 1
             )
    SELECT
            *
        FROM
            cte_recursive 

The results look like this

答案 1 :(得分:0)

使用聚合作为窗口函数的聚合非常简单。

您没有指定DBMS,因此以下内容适用于PostgreSQL。

创建测试表:

create table adi (id integer, postdate date, return integer);

insert into adi (id, postdate, return)
   values 
(1001, date '2013-07-01', 100),
(1001, date '2013-07-02', 101),
(1001, date '2013-07-03', 102),
(1001, date '2013-07-04', 103);

创建一个乘以所有值的聚合:

create aggregate mult(bigint) 
(
  sfunc = int8mul, 
  stype=bigint 
);

我们需要一个对行进行编号并保留第一行return值的结果。这可以使用窗口函数来完成:

select id, 
       postdate, 
       return,
       row_number() over (order by postdate) as rn,
       first_value(return) over (order by postdate) as base_value
from adi

通过这个结果,我们可以创建最终查询:

with numbered as (
  select id, 
         postdate, 
         return,
         row_number() over (order by postdate) as rn,
         first_value(return) over (order by postdate) as base_value
  from adi
)
-- only get the first row, so that the return value of that
-- is not included in the running aggregate
select id, 
       postdate, 
       return,
       return as linkedvalue
from numbered
where rn = 1

union all

select id, 
       postdate, 
       return,
       (mult(return) over (order by postdate))::numeric / (case when rn > 2 then base_value else 1 end)
from numbered
where rn > 1;

返回以下结果:

id   | postdate   | return | linkedvalue
-----+------------+--------+------------
1001 | 2013-07-01 |    100 |         100
1001 | 2013-07-02 |    101 |      101.00
1001 | 2013-07-03 |    102 |      103.02
1001 | 2013-07-04 |    103 |    10611.06

除自定义聚合函数外,查询几乎是标准的ANSI SQL。如何为正在使用的DBMS定义一个取决于该DBMS。

如果没有创建自定义聚合的能力,可以使用递归CTE完成。以下是标准ANSI SQL,没有任何特定于产品的语法:

with recursive numbered as (
  select id, 
         postdate, 
         return,
         row_number() over (order by postdate) as rn,
         first_value(return) over (order by postdate) as base_value
  from adi
), calc as (
  select id, postdate, return, return as linkedvalue, base_value, rn
  from numbered
  where rn = 1
  union all
  select c.id, c.postdate, c.return, 
         case when c.rn > 2 then (c.return * p.linkedvalue) else c.return end,
         p.base_value, 
         c.rn
  from numbered c
    join calc p on c.rn - 1 = p.rn
  where c.rn > 1
)
select id, postdate, return, 
       case when 
           rn > 2 then cast(linkedvalue as numeric) / 100 
           else linkedvalue 
       end as linkedvalue
from calc
order by postdate;