1

MS SQL Server 2014

Table schema (part of one) like following:

enter image description here

where Coinh1d_Close has type float.

For analytic purposes i need another column, more precisely computed column, Coinh1d_EMA12 based on Coinh1d_Close and previous value itself. First value is always known and calculates based on Coinh1d_Close only. The following values must to be calculated based on both Coinh1d_Close and previous value of the same column Coinh1d_EMA12, like in Excel:

Visualization of what i need in Excel

From Calculating value using previous value of a row in T-SQL i wrote some T-SQL expression

    ;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)

It gives exactly what i need

enter image description here

But my question is it possible to use previous value of Computed Column for the next value (i am using UDF function for Computed Column). I need like from this question Calculating value using previous value of a row in T-SQL but for Computed Column.

UPDATED

More information for clarification: this table consist of data for 1000 coins (BTC, ETH, etc.) and information for every coin starts with specific time Coinh1d_Time (Unix Timestamp) = 1346976000. At the begin we load to this table all info from this time to current time (about 2 000 000 records) and then every hour t-sql script updates this table (add 1000 row - new data per hour).

Also this table has many computed column (including one thet depends from Coinh1d_EMA12).

If it does not succeed to create Computed Column for Coinh1d_EMA12 i see solution: first of all create ordynary column Coinh1d_EMA12. Then one time update all table

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

Call for every coin:

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;

It takes about 30 minutes. And then create trigger for this column with above CTE code.

Konstantin
  • 796
  • 1
  • 11
  • 32
  • 4
    The **previous** rows is not defined without a unique sort order... There is no implicit order in SQL Server's tables... If you are using v2012+ you should read about `LAG()`, with earlier versions you might use `SELECT TOP 1 ...WHERE ... ORDER BY ...` to get the *closest* value. And please read [How to ask a good SQL question](http://meta.stackoverflow.com/questions/271055/tips-for-asking-a-good-structured-query-language-sql-question/271056) and [How to create a MCVE](http://stackoverflow.com/help/mcve) – Shnugo Mar 03 '18 at 10:59
  • Thank you for `LAG()`. As to good questions or not good I'll take care of without you! Good Luck! – Konstantin Mar 03 '18 at 11:14
  • 2
    Konstantin, I hope I get this wrong, but your comment sounds rather rude... You are on SO for more than 5 years and should know about *consumable data vs pictures*, you should know, that a tag `[sql-server]` is not enough, as the version is highly important... – Shnugo Mar 03 '18 at 11:48
  • previous value based on what order? – Zohar Peled Mar 04 '18 at 05:25
  • The data inserted into table strictly by `Coinh1d_Time` – Konstantin Mar 04 '18 at 09:39
  • @Konstantin What should happen If you delete or update a row? A SQL table isn't a cell calculator like Excel ... – Shnugo Mar 04 '18 at 12:35
  • @Konstantin can you be sure, that your *sort column* `Coinh1d_Time` will be unique in each and any case? Otherwise you'd be open to rather random results... – Shnugo Mar 04 '18 at 15:34

1 Answers1

0

Starting with the data from your function, you can use the LAG() (if on SQL Server 2012 or higher) window function to place a previous value in the same row. The code would follow this schema:

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

You can then use the PreviousRowFunctionComputedColumn in any expression you need.

EzLo
  • 13,780
  • 10
  • 33
  • 38