2016-05-05 209 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;