计算SQL Server

时间:2017-07-29 15:45:17

标签: sql-server

我们正在尝试计算滚动平均值,并试图转换大量的SO答案来解决问题。到目前为止,我们仍然没有成功。

我们尝试了什么

以下是我们考虑的一些SO答案。

我们最近的尝试是修改此处找到的解决方案之一(#4)。 https://www.red-gate.com/simple-talk/sql/t-sql-programming/calculating-values-within-a-rolling-window-in-transact-sql/

示例

以下是SQL Fiddle中的一个示例:http://sqlfiddle.com/#!6/4570a/17

在小提琴中,我们仍然试图让SUM工作正常但最终我们试图获得平均值。

最终目标

使用小提琴示例,我们需要找到Value1和ComparisonValue1之间的区别,并将其显示为Diff1。当一行没有Value1可用时,我们需要通过取最后两个Diff1值的平均值来估算它,然后将其添加到该行的ComparisonValue1。

使用正确的查询,结果如下所示:

GroupID  Number  ComparisonValue1  Diff1  Value1
5        10      54.78             2.41   57.19
5        11      55.91             2.62   58.53
5        12      55.93             2.78   58.71
5        13      56.54             2.7    59.24
5        14      56.14             2.74   58.88
5        15      55.57             2.72   58.29
5        16      55.26             2.73   57.99

问题:是否可以计算此平均值,因为它可能会影响以下行的平均值?

更新

  • 在Fiddle架构中添加了一个VIEW,以简化最终查询。
  • 更新了查询以包含Diff1的新滚动平均值(列Diff1Last2Avg)。这个滚动平均值很有效,直到我们在Value1列中遇到空值。这是我们需要插入估算值的地方。
  • 更新了query以包含没有Value1时应使用的估算值(列Value1Estimate)。如果我们可以在Value1列中使用估计值代替NULL,那么这种方法非常有效。由于Diff1列反映了Value1(或其估计值)与ComparisonValue1之间的差异,因此包括Estimate将填充Diff1中的所有NULL值。这反过来将继续允许计算未来行的估计。它在这一点上变得令人困惑,但仍然在抨击它。有什么想法吗?

1 个答案:

答案 0 :(得分:0)

这个想法归功于这个答案:来自@JesúsLópez的https://stackoverflow.com/a/35152131/6305294

我在代码中包含了注释来解释它。

<强>更新

  • 我已根据评论更正了查询。
  • 我在minuend和subtrahend交换数字以获得正数作为差异。
  • 删除了Diff2Ago列。

查询结果现在与您的示例输出完全匹配。

;WITH cte AS
(
    -- This is similar to your ItemWithComparison view
    SELECT i.Number, i.Value1, i2.Value1 AS ComparisonValue1,
        -- Calculated Differences; NULL will be returned when i.Value1 is NULL
        CONVERT( DECIMAL( 10, 3 ), i.Value1 - i2.Value1 ) AS Diff
    FROM Item AS i
            LEFT JOIN [Group] AS G ON g.ID = i.GroupID
            LEFT JOIN Item AS i2 ON i2.GroupID = g.ComparisonGroupID AND i2.Number = i.Number
    WHERE NOT i2.Id IS NULL
),
cte2 AS(
    /*
    Start with the first number

    Note if you do not have at least 2 consecutive numbers (in cte) with non-NULL Diff value and therefore Diff1Ago or Diff2Ago are NULL then everything else will not work;
    You may need to add additional logic to handle these cases */
    SELECT TOP 1 -- start with the 1st number (see ORDER BY)
            a.Number, a.Value1, a.ComparisonValue1, a.Diff, b.Diff AS Diff1Ago
    FROM cte AS a
            -- "1 number ago"
            LEFT JOIN cte AS b ON a.Number - 1 = b.Number
    WHERE NOT a.Value1 IS NULL
    ORDER BY a.Number
    UNION ALL
    SELECT b.Number, b.Value1, b.ComparisonValue1,
            ( CASE
                WHEN NOT b.Value1 IS NULL THEN b.Diff
                ELSE CONVERT( DECIMAL( 10, 3 ), ( a.Diff + a.Diff1Ago ) / 2.0 )
            END ) AS Diff,
        a.Diff AS Diff1Ago
    FROM cte2 AS a
        INNER JOIN cte AS b ON a.Number + 1 = b.Number
)
SELECT *, ( CASE WHEN Value1 IS NULL THEN ComparisonValue1 + Diff ELSE Value1 END ) AS NewValue1
FROM cte2 OPTION( MAXRECURSION 0 );

限制: 只有在需要考虑少量先前值时,此解决方案才能正常工作。