【问题标题】:T-SQL query update null valuesT-SQL 查询更新空值
【发布时间】:2012-05-18 14:28:20
【问题描述】:

我在 T-SQL 中有一个非常具体的问题。

如果我能解决我给你的这个示例案例,我想我将能够解决我原来的案例。

在表格中保存这些数据:

DECLARE @Test TABLE
(
    Value INT
    ,Date DATETIME2(7)
);

INSERT INTO @Test
VALUES
(NULL, '2011-01-01 10:00'),
(NULL, '2011-01-01 11:00'),
(2, '2011-01-01 12:00'),
(NULL, '2011-01-01 13:00'),
(3, '2011-01-01 14:00'),
(NULL, '2011-01-01 15:00'),
(NULL, '2011-01-01 16:00'),
(4, '2011-01-01 17:00'),
(NULL, '2011-01-01 18:00'),
(5, '2011-01-01 19:00'),
(6, '2011-01-01 20:00')

我需要选择这个输出:

Value   Date
2       2011-01-01 10:00
2       2011-01-01 11:00
2       2011-01-01 12:00
2       2011-01-01 13:00
3       2011-01-01 14:00
3       2011-01-01 15:00
3       2011-01-01 16:00
4       2011-01-01 17:00
4       2011-01-01 18:00
5       2011-01-01 19:00
6       2011-01-01 20:00

给出一些解释。如果某处的值为 NULL,我需要使用前一小时的值进行更新。如果一行中有多个空值,则具有非空值的最接近的较早时间将传播并填充所有这些空值。此外,如果一天的第一个小时为空,那么在这种情况下,具有非空值的当天最早的一小时会像 2 一样向下传播。在您的情况下,您可以假设至少一个值是非空值。

我的目标是用通用表表达式或类似的东西来解决这个问题。如果我尝试使用光标方式,我想我会在很短的时间内找到解决方案,但到目前为止我对 CTE 和递归 CTE 的尝试都失败了。

【问题讨论】:

  • 你试过什么?发布您到目前为止所做的事情会很有帮助 - 您可能已经很接近并且只需要一些建议来更正您现有的查询。
  • 因为我不知道一行中有多少个空值,所以前一小时的单个连接是不够的。在那里我想也许我可以通过某种方式递归解决它。我只使用经典层次结构方式的 CTE 递归,但我的想法是,如果我可以在递归中每次更新一个空值,也许我可以将它们全部填充。实际上我在尝试这个想法时很早就失去了它。所以我不相信会有什么帮助。
  • 在您的示例中,Values 不会随着 Date 的增加而减少。这是巧合,还是永远如此?
  • +1 用于实际包含工作表的“起点”和数据插入代码

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


【解决方案1】:

由于您的情况并不总是相同,这有点困难。在您的示例中,前两行需要从较晚日期的第一个值中获取它们的值,在其他情况下,它们需要从以前的日期中获取值。如果您总是需要查看以前的日期,您可以简单地执行以下查询:

SELECT  B.Value,
        A.[Date]
FROM @Test A
OUTER APPLY (SELECT TOP 1 *
             FROM @Test
             WHERE [Date] <= A.[Date] AND Value IS NOT NULL
             ORDER BY [Date] DESC) B

但在你的情况下,我认为你需要这个:

SELECT  ISNULL(B.Value,C.Value) Value,
        A.[Date]
FROM @Test A
OUTER APPLY (SELECT TOP 1 *
             FROM @Test
             WHERE [Date] <= A.[Date] AND Value IS NOT NULL
             ORDER BY [Date] DESC) B
OUTER APPLY (SELECT TOP 1 *
             FROM @Test
             WHERE Value IS NOT NULL
             ORDER BY [Date]) C

【讨论】:

  • 当我实际上接受另一个答案时,我有点快。它只适用于我每小时都在增加的样本数据。这是正确的答案,它给了我正确的结果。但是我的真实数据存在可怕的性能问题。您有什么建议可以提高性能吗?
  • @John - 是的,它的性能肯定会很差,因为你至少要通过表test 两次。你知道,第二个OUTER APPLY 仅在第一个值为NULL 时出现,也许你可以以更好的方式避免这种情况。我假设您至少在Date 列上有一个索引。
【解决方案2】:

试试这个:

select 
    t.value, t.date
      ,COALESCE(t.value
               ,(select MAX(tt.value) from @Test tt WHERE t.Date>tt.Date)
               ,(SELECT MIN(ttt.Value) FROM @Test ttt Where ttt.Date IS NOT NULL)
               ) AS UseValue
    from @Test   t

输出:

value       date                    UseValue
----------- ----------------------- -----------
NULL        2011-01-01 10:00:00.000 2
NULL        2011-01-01 11:00:00.000 2
2           2011-01-01 12:00:00.000 2
NULL        2011-01-01 13:00:00.000 2
3           2011-01-01 14:00:00.000 3
NULL        2011-01-01 15:00:00.000 3
NULL        2011-01-01 16:00:00.000 3
4           2011-01-01 17:00:00.000 4
NULL        2011-01-01 18:00:00.000 4
5           2011-01-01 19:00:00.000 5
6           2011-01-01 20:00:00.000 6

【讨论】:

  • 我起初接受了这个答案,但实际上我选择了上面的另一个解决方案,因为我认为两者都有效。但是现在当我在使用其他解决方案时遇到一些性能问题时,我开始尝试这个解决方案并发现它不起作用。它仅在此示例情况下有效,因为价值每小时都在增加。试着改变一下这些值,看看会发生什么:)
  • @John,如果你有一个适当的PK!
猜你喜欢
  • 1970-01-01
  • 2019-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-05
  • 1970-01-01
  • 1970-01-01
  • 2012-02-17
相关资源
最近更新 更多