SQL Server How to calculate different Trend line types in SQL function

ff29svar  于 2023-08-02  发布在  其他
关注(0)|答案(2)|浏览(101)

i have been seeking a good way to calculate excel like trendlines for my desktop app's dashboard graps.

most common trend types must be like

Type = 1 Linear Y = a + b*X
Type = 2 EXponential Y = a*e^(b*X)   
Type = 3 Logarithmic Y = a + b*ln(X)
Type = 4 Power Y = a*X^b

so here is how i solved my issue.

hm2xizp9

hm2xizp91#

As I mentioned in my other comment, this could be written as a inline TVF, instead of a multiline TVF. I've taken the OP's solution somewhat literally, however, a couple of CTEs allows us to do this:

CREATE FUNCTION dbo.tvf_TrendLine (@Type tinyint,
                                   @Rawdata XYTableType READONLY)
RETURNS TABLE
AS RETURN
    WITH xy AS(
    SELECT CONVERT(decimal(38,10),COUNT(*)) AS n,
           CONVERT(decimal(38,10),SUM(CASE WHEN @Type IN (3,4) THEN LOG(X) ELSE X END)) AS x,
           CONVERT(decimal(38,10),SUM(CASE WHEN @Type IN (3,4) THEN LOG(X) * LOG(X) ELSE X * X END)) AS x2,
           CONVERT(decimal(38,10),SUM(CASE WHEN @Type IN (2,4) THEN LOG(Y) ELSE Y END)) AS y,
           CONVERT(decimal(38,10),SUM(CASE WHEN @Type IN (2,4) THEN LOG(Y) * Y ELSE Y * Y END)) AS y2,
           CONVERT(decimal(38,10),SUM(CASE WHEN @Type = 2 THEN X * LOG(Y) WHEN @Type = 3 THEN LOG(X) * Y WHEN @Type = 4 THEN LOG(X) * LOG(Y) ELSE X * Y END)) AS xy
        FROM @RawData
        WHERE Y IS NOT NULL),
    ab AS(
        SELECT n,
               x,
               x2,
               y,
               Y2,
               xy,
               CONVERT(decimal(38,10),CASE WHEN @Type IN (2,4) THEN EXP((x2 * y - x * xy) / (n * x2 - x * x))
                                           ELSE (x2 * y - x * xy) / (n * x2 - x * x)
               END) AS a,
               CONVERT(decimal(38,10),(n * xy - x * y) / (n * x2 - x * x)) AS b
        FROM xy)
    SELECT rd.X,
           rd.Y,
           CASE
                WHEN @Type = 2 THEN ab.a * EXP(LOG(EXP(1)) * (ab.b * rd.X))
                WHEN @Type = 3 THEN ab.a + ab.b * LOG(rd.X)
                WHEN @Type = 4 THEN ab.a * EXP(LOG(rd.X) * ab.b)
                ELSE ab.a + ab.b * rd.X
           END AS Yt
    FROM @Rawdata rd
         CROSS JOIN ab;
GO

This returns exactly the same results as the OP's answer.

2wnc66cl

2wnc66cl2#

we need an input type to make it an SQL function

CREATE Type XYTableType 
AS TABLE (X float, Y float)

X can be a datetime if you want to work with time series. i try to make this function as simple as possible

create FUNCTION dbo.fn_TrendLine(@Type TINYINT, @raw_data XYTableType READONLY)
RETURNS @TrendTable TABLE(X float, Y float, Yt float)
AS
BEGIN
    DECLARE @n DECIMAL(38, 10),
        @x DECIMAL(38, 10),
        @x2 DECIMAL(38, 10),
        @y DECIMAL(38, 10),
        @xy DECIMAL(38, 10),
        @y2 DECIMAL(38, 10),
        @a DECIMAL(38, 10),
        @b DECIMAL(38, 10)

    SELECT  
    @n=COUNT(*),
    @x= sum(CASE
            WHEN @Type = 2 THEN X
            WHEN @Type = 3 THEN LOG(X)
            WHEN @Type = 4 THEN LOG(X)
            ELSE X
            END),
    @x2=sum(CASE
            WHEN @Type = 2 THEN X * X
            WHEN @Type = 3 THEN LOG(X) * LOG(X)
            WHEN @Type = 4 THEN LOG(X) * LOG(X)
            ELSE X * X
            END),
    @y= sum(CASE
            WHEN @Type = 2 THEN LOG(Y)
            WHEN @Type = 3 THEN Y
            WHEN @Type = 4 THEN LOG(Y)
            ELSE Y
            END),
    @xy=sum(CASE
            WHEN @Type = 2 THEN X * LOG(Y)
            WHEN @Type = 3 THEN LOG(X) * Y
            WHEN @Type = 4 THEN LOG(X) * LOG(Y)
            ELSE X * Y
            END),
    @y2=sum(CASE
            WHEN @Type = 2 THEN LOG(Y) * LOG(Y)
            WHEN @Type = 3 THEN Y * Y
            WHEN @Type = 4 THEN LOG(Y) * LOG(Y)
            ELSE Y * Y
            END)
    FROM @raw_data
    where Y is not null

    set @a = (@x2 * @y - @x * @xy) / (@n * @x2 - @x * @x)
    set @b = (@n * @xy - @x * @y) / (@n * @x2 - @x * @x )
    if @Type in (2,4)
        set @a = exp(@a)

    INSERT INTO @TrendTable(X, Y, Yt)
    SELECT  X,Y,
        Yt= case 
            WHEN @Type = 2 THEN @a *exp(log(exp(1))*(@b * X))
            WHEN @Type = 3 THEN @a + @b * LOG(X)
            WHEN @Type = 4 THEN @a * exp(log(X)*@b) 
            ELSE @a + @b * X
            end
    from @raw_data

    RETURN
END

here is an example

DECLARE @raw_data XYTableType
insert into @raw_data
values (1,1.15),(2,1.82),(3,3.13),(4,4.28),(5,4.67),(6,5.79),(7,7.81),(8,8.35),(9,9.40),(10,9.98),(11,5.79),(12,7.81),(13,8.35),(14,9.40),(15,null),(16,null)
select * from dbo.fn_TrendLine(1, @raw_data)
select * from dbo.fn_TrendLine(2, @raw_data)
select * from dbo.fn_TrendLine(3, @raw_data)
select * from dbo.fn_TrendLine(4, @raw_data)

x15 and x16 are there for extrapolations. you simply can copy results and paste it into an excel sheet to check with graphs or builtin excel functions.

important note: type 3,4 can't work with negative X values, 2,4 can't work with negative Y values. preparing the data is important before you calculate the certain types.

相关问题