2017-07-29 19 views
2

ローリング平均を計算しようとしており、数多くのSO答えを変換して問題を解決しようとしました。この時点まで、私たちはまだ失敗しています。SQL Serverで再帰的ローリング平均を計算する

は、私たちがを試してみた:

ここでは、私たちが考えたSOの答えの一部です。

当社の最新の試みは、ここで見つけるソリューション(#4)のいずれかを変更することでした。 https://www.red-gate.com/simple-talk/sql/t-sql-programming/calculating-values-within-a-rolling-window-in-transact-sql/

:ここ

はSQLフィドルの例である:フィドルでhttp://sqlfiddle.com/#!6/4570a/17

、我々はまだ右の仕事にSUMを取得しようとしているが、最終的に我々は、取得しようとしています平均。

最終目標フィドル例を使用して

は、我々はvalue1とComparisonValue1の違いを見つけて、DIFF1としてそれを提示する必要があります。行にValue1を使用できない場合は、最後の2つの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 

質問:それは潜在的に次の行の平均に織り込むことができるとき、この平均値を算出することができますか?

更新

  • は、最終的なクエリを簡素化するためにフィドルスキーマにVIEWを追加しました。
  • Diff1(列Diff1Last2Avg)の新しいローリング平均を含めるようにクエリを更新しました。このローリング平均は、Value1列でNULLを実行するまで有効です。ここで推定値を挿入する必要があります。
  • queryに、Value1(列Value1Estimate)がない場合に使用する見積もりを含めるように更新しました。これはうまくいっており、Value1列にNULLの代わりに見積もりを使用することができれば完璧です。 Diff1列にはValue1(またはその推定値)とComparisonValue1の差が反映されるため、Estimateを含めてDiff1のすべてのNULL値を入力します。これにより、引き続き将来の行の見積もりを計算できるようになります。この時点では混乱しますが、まだそれをハッキングしています。何か案は?アイデアのための
+0

可能であれば、レコードの数を減らし、問題のテーブル形式で期待される結果を追加します。良い質問 –

+0

どのバージョンのSQLですか? – Xedni

+0

@Xedni SQL Server 2008 –

答えて

0

クレジットがこの回答に行く:https://stackoverflow.com/a/35152131/6305294 @JesúsLópez

から、私はそれを説明するコードのコメントが含まれています。

UPDATE

  • 私はコメントに基づいてクエリを修正しています。
  • 私は負の数値と正の数値で差分を得るために減数を入れ替えました。
  • 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); 

制限事項: このソリューションを使用すると、値の前の小さな数を検討する必要があるときにのみ適しています。

+0

うわー、非常に近い。いくつかのマイナーな問題が更新されましたが、現在は[fiddle](http://sqlfiddle.com/#!6/4570a/41)で動作しています。しかし、結果は質問のサンプル結果と一致しません。最初のValue1ヌル値の見積もりは59.24である必要があります。私は質問を上に移動したときに何かがあったのですか?私はダブルチェックします。 –

+0

また、負の値がアプリケーションにとって重要なので、ABS機能を削除しました。 –

+0

再帰的なcte2クエリのCASE ELSE行は 'ELSE CONVERT(DECIMAL(10,3)、(a.Diff + a.Diff1Ago)/ 2.0)'である必要があります。 –

関連する問題