【问题标题】:T-SQL Compare previous non-NULL and current row to filter out by difference valueT-SQL 比较前一个非空行和当前行以按差异值过滤掉
【发布时间】:2015-04-19 21:57:15
【问题描述】:

我的示例数据库(实际上是 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 操作),以便在下一个查询中使用它。

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

我正在运行 SQL Server 2014。

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

【问题讨论】:

  • 您可以考虑使用自联接或递归 cte 来做一些事情,但这可能是使用光标的极少数情况之一。
  • 我和 Tab.在任何时候你都不应该使用游标,这可能是使用游标的时候了。

标签: sql-server tsql recursion comparison common-table-expression


【解决方案1】:

这样就可以了:

declare @tbl_1 table (eventdate date, val int)
insert into @tbl_1(eventdate,val) values
('20120323',3965),
('20120326',3979),
('20120327',3974),
('20120328',3965),
('20120329',3967),
('20120330',3959),
('20120402',3951),
('20120403',3961),
('20120404',3944),
('20120405',3935),
('20120409',3901),
('20120410',3822)

;With Ord as (
    select eventdate,val,ROW_NUMBER() OVER (ORDER BY eventdate) as rn
    from @tbl_1
), Runs as (
    select eventdate,val,val as prevval,rn
    from Ord
    where rn = 1
    union all
    select
        o.eventdate,
        CASE WHEN ABS(o.val - r.prevval) <= 12 THEN NULL ELSE o.val END,
        CASE WHEN ABS(o.val - r.prevval) <= 12 THEN r.prevval ELSE o.val END,
        o.rn
    from
        Runs r
            inner join
        Ord o
            on
                r.rn = o.rn - 1
)
SELECT
    r.eventdate,r.val
from Runs r

希望您能看到我们对两个新 CTE 所做的事情 - 第一个 (Ord) 只是很好地设置了一些东西,以便我们可以轻松地在“当前”行之间的 Runs 中执行自联接和上一个 - 您也许能够将其逻辑合并到当前 CTE 中,从而为我们提供此结果集以供使用。

Runs 中,我们还添加了一个新列 (prevval),它“记住”最后一个使用的非 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

如果仍然不清楚它做了什么,请将最终选择替换为SELECT *

eventdate  val         prevval     rn
---------- ----------- ----------- --------------------
2012-03-23 3965        3965        1
2012-03-26 3979        3979        2
2012-03-27 NULL        3979        3
2012-03-28 3965        3965        4
2012-03-29 NULL        3965        5
2012-03-30 NULL        3965        6
2012-04-02 3951        3951        7
2012-04-03 NULL        3951        8
2012-04-04 NULL        3951        9
2012-04-05 3935        3935        10
2012-04-09 3901        3901        11
2012-04-10 3822        3822        12

【讨论】:

  • 太棒了!非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多