在T-SQL中使用变量进行线性插值

时间:2015-07-28 10:03:01

标签: sql-server tsql linear-interpolation

输入我的程序

@time datetime -- Example: 27.07.2015 01:00

对于一些SELECT staments,我得到以下信息

@prevTime datetime -- Example: 27.07.2015 00:00
@prevValue real -- Example: 1
@nextTime datetime -- Example: 27.07.2015 02:00
@nextValue real -- Example: 3

现在我要计算

@value real -- In this Example the result should be: 2 

我想使用"线性插值",请看这里:https://en.wikipedia.org/wiki/Linear_interpolation

有人能告诉我,这怎么做得最好? (我可以将日期时间转换成真实的吗?)

编辑:

我的第一个快速和肮脏的想法:

SET @value = @prevValue + ((@nextValue-@prevValue)/(CAST(@nextTime as real)-CAST(@prevTime as real))) * (CAST(@time as real)-CAST(@prevTime as real));

不能工作,因为日期时间 - >真实不精确

我的第二个快速和肮脏的想法:

SET @value = @prevValue + ((@nextValue-@prevValue)/(DATEDIFF(second,@nextTime,GETDATE())-DATEDIFF(second,@prevTime,GETDATE()))) * (DATEDIFF(second,@time,GETDATE())-DATEDIFF(second,@prevTime,GETDATE()));

工作,但如果有人告诉我一个更好的方式,我会很高兴

2 个答案:

答案 0 :(得分:0)

我之前已将此类代码作为代码示例提交到SQL Server中心http://www.sqlservercentral.com/Forums/Topic872361-392-1.aspx

有问题的代码如下所示,执行标准的高斯消除法,将提供的数据点减少到一个等式,然后进行相应的推断:

-- Point type
CREATE TYPE Point AS TABLE (XCoordinate FLOAT, YCoordinate FLOAT);
GO

-- Curve fitting coefficient type
CREATE TYPE Coefficient AS TABLE (Multiplier FLOAT, PowerOfX INT);
GO

/**
 * Function:    fn_CurveFitPoints
 * Author:              Steven James Gray [ steve@cobaltsoftware.net ]
 * Date:                24th February, 2010
 * Version:             1.0
 * Description:
 *              Takes a series of points in a table variant in POINT (X float, Y float) format and
 *              computes the n-1 power series curve fit using the Gauss-Jordan elimination process.
 *              Return value is a series of Multiplier, Power elements that can be used for producing
 *              an estimated Y value for any X value.
 *      
 *              Please refer to fn_ExtrapolateYValue(@xvalue, @coefficients) for a simple implementation
 *              of how to use the output of this function.
 **/
CREATE FUNCTION dbo.fn_CurveFitPoints
        (
                @PointsToCurveFit POINT readonly
        )
        RETURNS @Result TABLE (Multiplier FLOAT, PowerOfX INT)
AS
BEGIN
        -- ==========================================================================================
        -- Stage 1 - Convert @PointsToFit into a matrix 
        -- ==========================================================================================
        DECLARE @Matrix TABLE (MatrixRow INT, MatrixColumn INT, MatrixValue FLOAT);
        DECLARE @TotalPoints INT  = (SELECT COUNT(1) FROM @PointsToCurveFit);

        WITH NumberProjectionCTE(CurrentNumber)
        AS
        (
                SELECT 1 
                UNION ALL
                SELECT 1+CurrentNumber FROM NumberProjectionCTE WHERE CurrentNumber < @TotalPoints
        ) INSERT INTO @Matrix
          SELECT 
                Sequence-1,                     -- Each point gets it's own row
                PWR.CurrentNumber-1,    -- Column per power of X
                CASE    
                        WHEN PWR.CurrentNumber = 1 -- 1st column is X^0 = 1 Always
                                THEN 1
                        ELSE POWER(XCoordinate,PWR.CurrentNumber-1)     -- Raise nth column to power n-1.
                END             
          FROM
                NumberProjectionCTE PWR,                -- Cross join numeric point data and column indexes
                (SELECT
                                ROW_NUMBER() OVER (ORDER BY XCoordinate, YCoordinate) AS Sequence,
                                XCoordinate,
                                YCoordinate
                        FROM
                                @PointsToCurveFit
                ) ValueData;

        /* Append Y values as nth column */
        INSERT INTO @Matrix
                SELECT
                                ROW_NUMBER() OVER (ORDER BY XCoordinate, YCoordinate) - 1 AS Sequence,
                                @TotalPoints,
                                YCoordinate
                        FROM
                                @PointsToCurveFit;

        -- ==========================================================================================
        -- Stage 2 - Compute row echelon form of matrix
        -- ==========================================================================================
        DECLARE @lead INT = 0, @index INT = 0, @current FLOAT;
        DECLARE @Rows INT = (SELECT MAX(MatrixRow) FROM @Matrix);
        DECLARE @Columns INT = (SELECT MAX(MatrixColumn) FROM @Matrix);
        DECLARE @Solved INT -- 0=Unsolvable, 1 = Solved

        DECLARE @R INT = 0
        WHILE @R <= @Rows
        BEGIN
                IF @Columns <= @lead 
                        BEGIN
                                -- Cannot solve this one
                                SET @Solved = 0;
                                BREAK;
                        END;

                SET @index = @R;

               -- Determine if any row swaps are needed.   
               WHILE (SELECT MatrixValue FROM @Matrix WHERE MatrixRow = @index AND MatrixColumn = @lead) = 0
                        BEGIN
                               SET @index = @index + 1;
                                IF @Rows = @index 
                                        BEGIN
                                                SET @index = @R;
                                                SET @lead = @lead + 1;
                                                IF @Columns = @lead 
                                                        BEGIN
                                                                -- Cannot solve
                                                                SET @Solved = 0;
                                                                BREAK;  
                                                        END;
                                        END;
                        END;

                -- Move this row to the correct position if needed.
                IF @index <> @R
                        BEGIN
                                -- Swap rows 
                                UPDATE @Matrix 
                                        SET MatrixRow = CASE MatrixRow
                                                                                WHEN @R THEN @index 
                                                                                WHEN @index THEN @R
                                                                        END
                                        WHERE MatrixRow IN (@index, @R);                                
                        END;

                -- Divide this row by it's lead column value, so that this row's lead is 1 (this will actually multiply/increase the value if lead <0)
                DECLARE @Divisor FLOAT = (SELECT MatrixValue FROM @Matrix WHERE MatrixRow = @R AND MatrixColumn = @lead);
                If @Divisor <> 1
                        BEGIN
                                UPDATE @Matrix SET MatrixValue = MatrixValue / @Divisor WHERE MatrixRow = @R;
                        END;


                -- Update other rows and divide them by the appropriate multiple of this row in order to zero the current lead column.
                UPDATE I
                        SET
                                MatrixValue = I.MatrixValue - (M.MatrixValue * R.MatrixValue)
                        FROM
                                @Matrix I
                                        INNER JOIN @Matrix M ON M.MatrixRow = I.MatrixRow AND M.MatrixColumn = @lead 
                                        INNER JOIN @Matrix R ON R.MatrixColumn = I.MatrixColumn AND R.MatrixRow = @R AND R.MatrixRow <> I.MatrixRow

                            SET @lead = @lead + 1;

                -- Move to next
                SET @R = @R + 1;
        END;

        -- If we didn't bomb out, we're solved.
        IF @Solved IS NULL
                BEGIN
                        SET @Solved = 1
                END;            


        -- ==========================================================================================
        -- Stage 3 - Produce coefficients list (The final colum when in REF)
        -- ==========================================================================================
        IF @Solved = 1 
                BEGIN
                        INSERT INTO @Result (Multiplier, PowerOfX)              
                                SELECT 
                                        MatrixValue,
                                        MatrixRow 
                                FROM @Matrix 
                                WHERE MatrixColumn = @Columns;
                END;

        RETURN;
END;
GO
CREATE FUNCTION dbo.fn_ExtrapolateYValue
(
    @XValue FLOAT,
    @Coefficients Coefficient readonly
)
RETURNS FLOAT
AS
BEGIN
    RETURN (SELECT SUM(Multiplier * POWER(@XValue, PowerOfX)) FROM @Coefficients);
END

例如:

DECLARE @PointsToCurveFit Point

-- A few simple X/Y values
INSERT INTO @PointsToCurveFit SELECT    1   ,   6
INSERT INTO @PointsToCurveFit SELECT    2   ,   3
INSERT INTO @PointsToCurveFit SELECT    3   ,   2

-- Calculate the curve fitting coefficients
DECLARE @Coefficients Coefficient 
INSERT INTO @Coefficients SELECT * FROM dbo.fn_CurveFitPoints(@PointsToCurveFit);

-- Shows that y= 11x^0 + 6x + x^2
SELECT * FROM @Coefficients;

-- Show the values for X=-5 to 5
WITH NumberCTE(Number)
AS
(
    SELECT -5
    UNION ALL
    SELECT 1 + Number FROM NumberCTE WHERE Number < 5
) SELECT 
    Number AS XValue, 
    dbo.fn_ExtrapolateYValue(Number, @Coefficients) AS YValue 
FROM NumberCTE;

在指定的代码中,我在X轴范围内推断曲线的函数从-5到+5。

答案 1 :(得分:0)

我对引擎系统的追求,降低焊接强度的方法,这就是我解决问题的方法-干净,简单。

    CREATE TABLE WeldStrengthReduction
    (
     [Temperature] numeric(18,4) NOT NULL ,
     [Reduction]   numeric(18,4) NOT NULL ,
    );
    GO

    insert into WeldStrengthReduction (Temperature,Reduction) 
    values 
    (510,1),
    (538,0.95),
    (566,0.91),
    (593,0.86),
    (621,0.82),
    (649,0.77),
    (677,0.73),
    (704,0.68),
    (732,0.64),
    (760,0.59),
    (788,0.55),
    (816,0.5);
    Go

    Create Function WSRF(@Tempreture Numeric(18,4) ) returns Numeric(18,4)
    as
    begin
    declare 
        @X1 Numeric(18,4),
        @X2 Numeric(18,4),
        @X3 Numeric(18,4),
        @Y1 Numeric(18,4),
        @Y2 Numeric(18,4),
        @Y3 Numeric(18,4),
        @pointer int

        set @X2 = @Tempreture

        declare @Templist table (id int IDENTITY(1,1), temp numeric(18,4), red numeric(18,4))
        insert into @Templist select Temperature,Reduction from WeldStrengthReduction order by Temperature

        select top 1 @X3 = temp, @Y3 = red, @pointer = id from @Templist where temp >= @Tempreture
        if @pointer = 1 return @Y3 -- if incomming tempereture is below lowest, return according to lowewst temp
        if @pointer is null return null -- if incomming tempereture is above highest, return null 
        select @X1 = temp, @Y1 = red from @Templist where id = @pointer - 1

        set @Y2 = ((@X2-@X1)*(@Y3-@Y1))/(@X3 - @X1) + @Y1
        return @Y2
    end;
    Go

    select WSRF(772);
    select WSRF(300);
    select WSRF(1200);