web-dev-qa-db-ja.com

T-SQL CTE前のNULL以外の行と現在の行を比較して、差分値で除外します

私のサンプルデータベース(実際にはCTEステートメントデータ)は次のようになります。

_eventdate   val
2012-03-23  3965
2012-03-26  3979
2012-03-27  3974
2012-03-28  3965
2012-03-29  3967
2012-03-30  3959
2012-04-02  3951
2012-04-03  3961
2012-04-04  3944
2012-04-05  3935
2012-04-09  3901
2012-04-10  3822
_

差が12未満の値を削除したい。これは私のクエリと出力です:

_SELECT
eventdate,
CASE
WHEN ABS(val - LAG(val) OVER (ORDER BY eventdate)) <= 12
THEN NULL
ELSE val
END AS val
FROM tbl_1
ORDER BY eventdate
_

出力:

_eventdate   val
2012-03-23  3965
2012-03-26  3979
2012-03-27  NULL
2012-03-28  NULL
2012-03-29  NULL
2012-03-30  NULL
2012-04-02  NULL
2012-04-03  NULL
2012-04-04  3944
2012-04-05  NULL
2012-04-09  3901
2012-04-10  3822
_

問題は、現在の行と前の行を比較することです。現在と以前の非NULL値を比較する必要があります。つまり、前のステップでNULLにされなかった最後の値を意味します。

これは私が必要なものです:

_eventdate   val
2012-03-23  3965
2012-03-26  3979
2012-03-27  NULL
2012-03-28  3965
2012-03-29  NULL
2012-03-30  NULL
2012-04-02  3951
2012-04-03  NULL
2012-04-04  NULL
2012-04-05  3935
2012-04-09  3901
2012-04-10  3822
_

以下を試しました。上記のように自己参照CTEクエリを作成し、_THEN NULL_をTHEN LAG(val) OVER (ORDER BY eventdate)に置き換えて、最後の適切な値をコピーし、次の値と比較しました。次に、重複を削除します。しかし、それはOPTION (MAXRECURSION 0)で致命的なループを起こし、結果はまったくありません。

これはCURSORだけで実行できるようです。次のクエリで使用するには、結果セットをCTEステートメント(CTE内)として必要です。

私がCURSORの例を探した限り、それらはすべてCTEへの最終的なSELECTとして行われます。これは私の選択肢ではありません!

SQL Server 2014を実行しています。

あなたの助けや考えに感謝します!

2
84RR1573R

以下は、Paul Whiteがブログで取り上げた Performance Tuning the Whole Query Plan の手法を使用した再帰CTEソリューションです。

declare @T table
(
  Eventdate date index IX_Eventdate clustered,
  Val int
);

insert into @T(Eventdate, Val) values
('2012-03-23',  3965),
('2012-03-26',  3979),
('2012-03-27',  3974),
('2012-03-28',  3965),
('2012-03-29',  3967),
('2012-03-30',  3959),
('2012-04-02',  3951),
('2012-04-03',  3961),
('2012-04-04',  3944),
('2012-04-05',  3935),
('2012-04-09',  3901),
('2012-04-10',  3822);

with C as
(
  select top(1) 
         T.Eventdate,
         T.Val,
         T.Val as PrevVal1,
         T.Val as PrevVal2
  from @T as T
  order by T.eventdate
  union all
  select T.Eventdate,
         T.Val,
         T.PrevVal1,
         T.PrevVal2
  from (
       select T.Eventdate,
              T.Val,
              iif(abs(T.Val - C.PrevVal1) <= 12, C.PrevVal1, T.Val) as PrevVal1,
              iif(abs(T.Val - C.PrevVal1) <= 12, null, T.Val) as PrevVal2,
              row_number() over(order by T.Eventdate) as rn
       from @T as T
       inner join C
         on C.Eventdate < T.Eventdate
       ) as T
  where T.rn = 1
)
select C.Eventdate,
       C.PrevVal2 as Val
from C
order by C.Eventdate
option (maxrecursion 0);
5
Mikael Eriksson