T-SQL CTE 比较前一个非空行和当前行以按差值过滤掉

84R*_*73R 2 sql-server t-sql cursors recursive

我的示例数据库(实际上是 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
Run Code Online (Sandbox Code Playgroud)

我想删除差异小于 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
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

问题是它在当前行和前一行之间进行比较。我需要比较当前和以前的非 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
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法。我做了一个自我引用CTE查询如上替代THEN NULLTHEN LAG(val) OVER (ORDER BY eventdate)复制上次合适的值,并与下一个比较。然后删除重复项。但它致命地循环OPTION (MAXRECURSION 0),根本没有结果。

似乎这只能由 CURSOR 完成。我需要结果集作为 CTE 语句(在 CTE 内),以便在下一个查询中使用它。

就我寻找的 CURSOR 示例而言,它们都是作为SELECTCTE的最终完成的。这不是我的选择!

我正在运行 SQL Server 2014。

非常感谢您的帮助或想法!

Mik*_*son 5

这是一个递归 CTE 解决方案,它使用了 Paul White 在博客性能调优整个查询计划中介绍的技术

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);
Run Code Online (Sandbox Code Playgroud)