次の入力があります。
id | value
----+-------
1 | 136
2 | NULL
3 | 650
4 | NULL
5 | NULL
6 | NULL
7 | 954
8 | NULL
9 | 104
10 | NULL
次の結果が期待されます。
id | value
----+-------
1 | 136
2 | 136
3 | 650
4 | 650
5 | 650
6 | 650
7 | 954
8 | 954
9 | 104
10 | 104
簡単な解決策は、テーブルを<
リレーションで結合してから、a のMAX
値を選択することですGROUP BY
。
WITH tmp AS (
SELECT t2.id, MAX(t1.id) AS lastKnownId
FROM t t1, t t2
WHERE
t1.value IS NOT NULL
AND
t2.id >= t1.id
GROUP BY t2.id
)
SELECT
tmp.id, t.value
FROM t, tmp
WHERE t.id = tmp.lastKnownId;
ただし、このコードを簡単に実行すると、入力テーブルの行数の2乗(O(n ^ 2))が内部的に作成されます。私はt-sqlがそれを最適化することを期待していました-ブロック/レコードレベルでは、行うべきタスクは非常に簡単で線形であり、本質的にforループ(O(n))です。
ただし、私の実験では、最新のMS SQL 2016はこのクエリを正しく最適化できないため、このクエリを大きな入力テーブルに対して実行することはできません。
さらに、クエリを迅速に実行する必要があり、同様に簡単な(ただし非常に異なる)カーソルベースのソリューションを実行不可能にします。
いくつかのメモリバックアップされた一時テーブルを使用することは良い妥協策となる可能性がありますが、サブクエリを使用したサンプルクエリが機能しなかったため、大幅に高速に実行できるかどうかはわかりません。
また、t-sqlドキュメントからウィンドウ関数を掘り下げることも考えています。たとえば、累積合計は非常によく似ていますが、前の要素の合計ではなく、最新の非ヌル要素を提供するようにだますことができませんでした。
理想的な解決策は、手続き型コードや一時テーブルを使用しない迅速なクエリです。または、一時テーブルを使用したソリューションでも問題ありませんが、テーブルを手続き的に繰り返すことはできません。