2016-05-05 243 views
0

下面的产品,我需要使用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

您使用的是什么rdbms? –

+0

将自定义聚合作为窗口函数应该很容易。你如何定义行的顺序?在一个关系数据库中,除非你有'按顺序'的东西,否则不存在“第一行”。您的示例中行的顺序是由“postdate”列定义的吗?或不同的列? –

+1

这个例子不清楚。可能是LinkedValues是100,10100,1030200,106110600? – knagaev

回答

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

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;