【问题标题】:Last_value with IGNORE NULLS in SQL ServerSQL Server 中带有 IGNORE NULLS 的 Last_value
【发布时间】:2017-12-07 05:14:15
【问题描述】:

我有一个具有空值的时间序列。我想用最近的非非值替换每个空值。根据我的研究,Oracle SQL 可以使用带有 IGNORE NULLS 的 Last_value 轻松完成此任务。是否有类似的方法可以使用 SQL Server 2016 完成此任务?否则我只会使用 C# 编写代码,但感觉使用 SQL 会更快、更简洁、更容易。

Sec SCORE
1   Null
2   Null
3   5
4   Null
5   8
6   7
7   Null

应替换为:

Sec SCORE
1   Null
2   Null
3   5
4   5
5   8
6   7
7   7

【问题讨论】:

标签: c# sql sql-server


【解决方案1】:

您可以通过两个累积操作来做到这一点:

select t.*,
       coalesce(score, max(score) over (partition by maxid)) as newscore
from (select t.*,
             max(case when score is not null then id end) over (order by id) as maxid
      from t
     ) t;

最里面的子查询获取有值的最近的 id。最外层将该值“传播”到后续行。

如果您真的想更新表格,您可以轻松地将其合并到update 中。但是,Oracle 不能(很容易)做到这一点,所以我猜这没有必要......

【讨论】:

  • 天才!正是我需要的。但是,我计划经常在数百万行上执行此操作。对 300 万条记录执行上述操作大约需要 1 分钟。如果我切换到 Oracle Express 并将 last_value 与 IGNORE NULLS 一起使用,我会获得显着的速度优势吗?
  • @TrevorD 。 . .你必须自己测试一下。当然,运行两个窗口函数比运行一个需要更长的时间;我不确定是否值得为此付出努力切换数据库。
  • 该死的,这真是太好了。可悲的是,我永远写不出这样好的 SQL:@
  • 它只取最大值而不是如果在 null 之前有任何最小值,理想情况下它应该在 null 值之前捕获
  • @MayankAwasthi 。 . .这回答了这里提出的问题。如果您有自己的问题,请作为新问题提出。顺便说一句,这不是计算累积最大值。查看问题中的数据,这可以满足 OP 的要求。
【解决方案2】:

如果性能是一个问题,我建议使用这篇文章的解决方案:

The Last non NULL Puzzle

他的最终解决方案虽然密集,但在没有任何连接的线性查询计划中表现出色。这是我使用的一个示例实现,它通过 type2 scd 临时表携带最后一个客户名称。在这个临时表中,NULL 表示没有更新,'*** DELETED ***' 表示显式设置为 NULL。以下将其清理为非常类似于实际的 SCD 记录:

WITH [SampleNumbered] AS (
    SELECT  *, ROW_NUMBER() OVER ( PARTITION BY [SampleId] ORDER BY [StartDatetime] ) AS [RowNumber]
    FROM [dbo].[SampleDimStage]
), [SamplePrep] AS (
    SELECT  [SampleId]
        ,   [StartDatetime]
        ,   CAST([RowNumber] AS BINARY(8)) + CAST([SampleGroupId] AS VARBINARY(255)) AS [BinarySampleGroupId]
        ,   CAST([RowNumber] AS BINARY(8)) + CAST([SampleStatusCode] AS VARBINARY(255)) AS [BinarySampleStatusCode]
    FROM [SampleNumbered]
), [SampleCleanUp] AS (
    SELECT  [SampleId]
        ,   [StartDatetime]
        ,   CAST(SUBSTRING(MAX([BinarySampleGroupId]) OVER( PARTITION BY [SampleId] ORDER BY [StartDatetime] )
                , 9, 255) AS VARCHAR(255)) AS [LastSampleGroupId]
        ,   CAST(SUBSTRING(MAX([BinarySampleStatusCode]) OVER( PARTITION BY [SampleId] ORDER BY [StartDatetime] )
                , 9, 255) AS VARCHAR(255)) AS [LastSampleStatusCode]
        ,   LEAD([StartDatetime]) OVER( PARTITION BY [SampleId] ORDER BY [StartDatetime] ) AS [EndDatetime]
    FROM [SamplePrep]
)
SELECT  CAST([SampleId] AS NUMERIC(18)) AS [SampleId]
    ,   CAST(NULLIF([sc].[LastSampleGroupId],'*** DELETED ***') AS NUMERIC(18)) AS [GroupId]
    ,   CAST(NULLIF([sc].[LastSampleStatusCode],'*** DELETED ***') AS CHAR(3)) AS [SampleStatusCode]
    ,   [StartDatetime]
    ,   [sc].[EndDatetime]
FROM [SampleCleanUp] [sc];

如果您的排序键是某种整数,您可以完全跳过第一个 CTE 并将其直接转换为二进制。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 2012-11-21
    • 2010-11-30
    • 2013-11-06
    • 2020-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多