带递归的SQL Server计算列

时间:2018-03-03 10:13:02

标签: sql-server tsql sql-server-2014 common-table-expression calculated-columns

MS SQL Server 2014

表模式(一部分)如下所示:

enter image description here

其中Coinh1d_Close的类型为 float。

出于分析目的,我需要另一列,更精确地计算列,Coinh1d_EMA12基于Coinh1d_Close和先前值本身。第一个值始终是已知的,仅基于Coinh1d_Close计算。必须根据Coinh1d_Close和同一列Coinh1d_EMA12的上一个值计算以下值,例如在Excel中:

Visualization of what i need in Excel

Calculating value using previous value of a row in T-SQL我写了一些T-SQL表达式

    ;with cteCalculation as (
select t.Coinh1d_Id, t.Coinh1d_Time, t.Coinh1d_Close, t.Coinh1d_Close as Column2
    from Coinsh1d t
    where t.Coinh1d_Id in (1)
union all
select t.Coinh1d_Id, t.Coinh1d_Time, t.Coinh1d_Close, cast(t.Coinh1d_Close as float)*cast(2 as float)/(cast(12 as float)+cast(1 as float))+cast(c.Column2 as float)*(cast(1 as float)-cast(2 as float)/(cast(12 as float)+cast(1 as float))) as Column2
    from Coinsh1d t
        inner join cteCalculation c
            on t.Coinh1d_Id-1 = c.Coinh1d_Id
    where t.Coinh1d_Name='BTC'
) select c.Coinh1d_Id, c.Coinh1d_Time, c.Coinh1d_Close, c.Column2 
from cteCalculation c option (maxrecursion 0)

它完全符合我的需要

enter image description here

但是我的问题是可以使用计算列的先前值作为下一个值(我使用计算列的UDF函数)。我需要来自这个问题Calculating value using previous value of a row in T-SQL,但是对于Computed Column。

已更新

更多信息需要澄清:此表包含1000个硬币(BTC,ETH等)的数据,每个硬币的信息以特定时间Coinh1d_Time(Unix时间戳)= 1346976000开头。在开始时我们加载从这个时间到当前时间(约2 000 000条记录)的所有信息,然后每小时t-sql脚本更新此表(添加1000行 - 每小时新数据)。

此表还有许多计算列(包括一个取决于Coinh1d_EMA12)。

如果不能为Coinh1d_EMA12创建计算列,我会看到解决方案:首先创建ordynary列Coinh1d_EMA12。然后一次更新所有表

ALTER PROCEDURE [dbo].[UpdateEMA12h1d_notused] 
-- Add the parameters for the stored procedure here
@Coin varchar(50)
AS
BEGIN
    SET NOCOUNT ON;

print @coin

;with cte as (
select 
    t.Coinh1d_Id, t.Coinh1d_Close, t.Coinh1d_Close as Column2
from 
    Coinsh1d t
where 
    t.Coinh1d_Id in (select MIN(Coinh1d_Id) from Coinsh1d where Coinh1d_Name=@Coin)
union all
select 
    t.Coinh1d_Id, t.Coinh1d_Close, cast(t.Coinh1d_Close as float)*cast(2 as float)/(cast(12 as float)+cast(1 as float))+cast(c.Column2 as float)*(cast(1 as float)-cast(2 as float)/(cast(12 as float)+cast(1 as float))) as Column2
from 
    Coinsh1d t
inner join 
    cte c
on 
    t.Coinh1d_Id-1 = c.Coinh1d_Id
where 
    t.Coinh1d_Name=@Coin
)
select 
    c.Coinh1d_Id, c.Column2 
into 
    #tempEMA12
from 
    cte c 
option 
    (maxrecursion 0)

update
    t1
set
    t1.Coinh1d_Ema12 = t2.Column2
from
    Coinsh1d as t1
inner join 
    #tempEMA12 as t2
on 
    t1.Coinh1d_Id = t2.Coinh1d_Id

drop table #tempEMA12
END

要求每一枚硬币:

DECLARE @MyCursor CURSOR;
DECLARE @MyField varchar(50);
BEGIN
    SET @MyCursor = CURSOR FOR
    select Coin_Symbol from Coins

OPEN @MyCursor 
FETCH NEXT FROM @MyCursor 
INTO @MyField

WHILE @@FETCH_STATUS = 0
BEGIN     

  exec UpdateEMA12h1d_notused @MyField

  FETCH NEXT FROM @MyCursor INTO @MyField 
END; 

    CLOSE @MyCursor ;
    DEALLOCATE @MyCursor;
END;

大约需要30分钟。然后使用上面的CTE代码为此列创建触发器。

1 个答案:

答案 0 :(得分:0)

从函数中的数据开始,您可以使用LAG()(如果在SQL Server 2012或更高版本上)窗口函数将前一个值放在同一行中。代码将遵循此架构:

SELECT
  C.CoinID,
  C.CoinTime,
  C.CoinClose,
  C.FunctionComputedColumn,

  PreviousRowFunctionComputedColumn =
    LAG(
      C.FunctionComputedColumn, -- The column you need from the previous row
      1, -- How many rows back you need to go
      0) -- Which value should it take if there is no previous row
   OVER (
      PARTITION BY
        CoinID -- Row ordering resets with each different CoinID
      ORDER BY
        CoinTime ASC) -- Since it's ascending, the previous one is older
FROM
  CoinData AS C
ORDER BY
  C.CoinID,
  C.CoinTIme

然后,您可以在所需的任何表达式中使用PreviousRowFunctionComputedColumn