SQL 用 hold 填补空白
SQL fill gaps with hold
我遇到了一个我无法用我的知识解决的问题,而且我还没有找到任何我理解的足以解决我的问题的解决方案。
这就是我努力实现的目标。
我有一个具有以下结构的数据库:
node_id, source_time, value
1 , 10:13:15 , 1
2 , 10:13:15 , 1
2 , 10:13:16 , 2
1 , 10:13:19 , 2
1 , 10:13:25 , 3
2 , 10:13:28 , 3
我想要一个 sql 查询来获得以下输出
time , value1, value2
10:13:15, 1 , 1
10:13:16, 1 , 2
10:13:19, 2 , 2
10:13:25, 3 , 2
10:13:28, 3 , 3
你看,时间是从两个节点发生的所有时间。
但是由于 node1 在时间 :16 和 :28 没有值,因此必须填补空白。
我得到了从一列 table 中得到两列的地步。那不是困难的部分。
SELECT T1.[value], T2.[value]
FROM [db1].[t_value_history] T1, [db1].[t_value_history] T2
WHERE ( T1.node_id = 1 AND T2.node_id = 2)
但是结果看起来不像我想要的那样。
我找到了带有 COALESCE 的东西和另一个 table 的东西,它保存了以前的值。但是对于如此简单的事情来说,这看起来很复杂。
我想有一个简单的 sql 解决方案,但我没有太多时间来研究材料。
我很乐意知道要使用哪个函数。
到目前为止谢谢。
编辑:更改数据库,最后一行出错。
Edit2:我正在使用 SQL 服务器。很抱歉没有澄清这一点。此外,这些值不一定会增加。我只是在此处的示例中使用了递增的数字。
您可以使用条件聚合来获取正确的行集:
select vh.source_time,
max(case when vh.node_id = 1 then value end) as value_1,
max(case when vh.node_id = 2 then value end) as value_2
from db1.t_value_history vh
group by vh.source_time;
如果要填写值,那么最好的解决方案是lag()
和ignore nulls
。由 ANSI 支持,但 SQL 服务器(我猜你正在使用)不支持。你的价值似乎在增加。如果是这种情况,您可以使用累积最大值:
select vh.source_time,
max(max(case when vh.node_id = 1 then value end)) over (order by vh.source_time) as value_1,
max(max(case when vh.node_id = 2 then value end) over (order by vh.source_time) as value_2
from db1.t_value_history vh
group by vh.source_time;
在您的数据中,value
在增加,因此这适用于您示例中的数据。如果不是这种情况,则需要更复杂的查询来填补空白。
这将在 SQL 服务器中完成。虽然不是 'nice':
SELECT DISTINCT
T1.source_time,
CASE WHEN T1.node_id = 1 THEN T1.[value] ELSE ISNULL(T2.[value], T3.[value]) END,
CASE WHEN T1.node_id = 1 THEN ISNULL(T2.[value], T3.[Value]) ELSE T1.[value] END
FROM
[db1].[t_value_history] T1
LEFT OUTER JOIN [db1].[t_value_history] T2 ON T2.source_time = T1.source_time
AND T2.node_id <> T1.node_id -- This join looks for a value for the other node at the same time.
LEFT OUTER JOIN [db1].[t_value_history] T3 ON T3.source_time < T1.source_time
AND T3.node_id <> T1.node_id -- If the previous join is empty, this looks for values for the other node at previous times
LEFT OUTER JOIN [db1].[t_value_history] T4 ON T4.source_time > T3.source_time
AND T4.source_time < T1.source_time
AND T4.node_id <> T1.node_id -- This join makes sure there aren't any more recent values
WHERE
T4.node_id IS NULL
这适用于 SQL 服务器。如果您确定两个节点的最短时间都有一个值,那么您可以将 OUTER APPLY 更改为 CROSS APPLY,这样性能会更好。
WITH times
AS ( SELECT DISTINCT
source_time
FROM dbo.t_value_history
)
SELECT t.source_time ,
n1.value ,
n2.value
FROM times AS t
OUTER APPLY ( SELECT TOP 1
h.value
FROM dbo.t_value_history AS h
WHERE h.node_id = 1
AND h.source_time <= t.source_time
ORDER BY h.source_time DESC
) AS n1
OUTER APPLY ( SELECT TOP 1
h.value
FROM dbo.t_value_history AS h
WHERE h.node_id = 2
AND h.source_time <= t.source_time
ORDER BY h.source_time DESC
) AS n2;
我遇到了一个我无法用我的知识解决的问题,而且我还没有找到任何我理解的足以解决我的问题的解决方案。
这就是我努力实现的目标。 我有一个具有以下结构的数据库:
node_id, source_time, value
1 , 10:13:15 , 1
2 , 10:13:15 , 1
2 , 10:13:16 , 2
1 , 10:13:19 , 2
1 , 10:13:25 , 3
2 , 10:13:28 , 3
我想要一个 sql 查询来获得以下输出
time , value1, value2
10:13:15, 1 , 1
10:13:16, 1 , 2
10:13:19, 2 , 2
10:13:25, 3 , 2
10:13:28, 3 , 3
你看,时间是从两个节点发生的所有时间。 但是由于 node1 在时间 :16 和 :28 没有值,因此必须填补空白。 我得到了从一列 table 中得到两列的地步。那不是困难的部分。
SELECT T1.[value], T2.[value]
FROM [db1].[t_value_history] T1, [db1].[t_value_history] T2
WHERE ( T1.node_id = 1 AND T2.node_id = 2)
但是结果看起来不像我想要的那样。
我找到了带有 COALESCE 的东西和另一个 table 的东西,它保存了以前的值。但是对于如此简单的事情来说,这看起来很复杂。 我想有一个简单的 sql 解决方案,但我没有太多时间来研究材料。
我很乐意知道要使用哪个函数。
到目前为止谢谢。
编辑:更改数据库,最后一行出错。
Edit2:我正在使用 SQL 服务器。很抱歉没有澄清这一点。此外,这些值不一定会增加。我只是在此处的示例中使用了递增的数字。
您可以使用条件聚合来获取正确的行集:
select vh.source_time,
max(case when vh.node_id = 1 then value end) as value_1,
max(case when vh.node_id = 2 then value end) as value_2
from db1.t_value_history vh
group by vh.source_time;
如果要填写值,那么最好的解决方案是lag()
和ignore nulls
。由 ANSI 支持,但 SQL 服务器(我猜你正在使用)不支持。你的价值似乎在增加。如果是这种情况,您可以使用累积最大值:
select vh.source_time,
max(max(case when vh.node_id = 1 then value end)) over (order by vh.source_time) as value_1,
max(max(case when vh.node_id = 2 then value end) over (order by vh.source_time) as value_2
from db1.t_value_history vh
group by vh.source_time;
在您的数据中,value
在增加,因此这适用于您示例中的数据。如果不是这种情况,则需要更复杂的查询来填补空白。
这将在 SQL 服务器中完成。虽然不是 'nice':
SELECT DISTINCT
T1.source_time,
CASE WHEN T1.node_id = 1 THEN T1.[value] ELSE ISNULL(T2.[value], T3.[value]) END,
CASE WHEN T1.node_id = 1 THEN ISNULL(T2.[value], T3.[Value]) ELSE T1.[value] END
FROM
[db1].[t_value_history] T1
LEFT OUTER JOIN [db1].[t_value_history] T2 ON T2.source_time = T1.source_time
AND T2.node_id <> T1.node_id -- This join looks for a value for the other node at the same time.
LEFT OUTER JOIN [db1].[t_value_history] T3 ON T3.source_time < T1.source_time
AND T3.node_id <> T1.node_id -- If the previous join is empty, this looks for values for the other node at previous times
LEFT OUTER JOIN [db1].[t_value_history] T4 ON T4.source_time > T3.source_time
AND T4.source_time < T1.source_time
AND T4.node_id <> T1.node_id -- This join makes sure there aren't any more recent values
WHERE
T4.node_id IS NULL
这适用于 SQL 服务器。如果您确定两个节点的最短时间都有一个值,那么您可以将 OUTER APPLY 更改为 CROSS APPLY,这样性能会更好。
WITH times
AS ( SELECT DISTINCT
source_time
FROM dbo.t_value_history
)
SELECT t.source_time ,
n1.value ,
n2.value
FROM times AS t
OUTER APPLY ( SELECT TOP 1
h.value
FROM dbo.t_value_history AS h
WHERE h.node_id = 1
AND h.source_time <= t.source_time
ORDER BY h.source_time DESC
) AS n1
OUTER APPLY ( SELECT TOP 1
h.value
FROM dbo.t_value_history AS h
WHERE h.node_id = 2
AND h.source_time <= t.source_time
ORDER BY h.source_time DESC
) AS n2;