使用SQL服务器,我有一个类似于以下内容的表:
id | time | measurement
---+---------------------+-------------
1 | 2014-01-01T05:00:00 | 1.0
1 | 2014-01-01T06:45:00 | 2.0
1 | 2014-01-01T09:30:00 | 3.0
1 | 2014-01-01T11:00:00 | NULL
1 | 2014-02-05T03:00:00 | 1.0
1 | 2014-02-05T05:00:00 | NULL
在为同一个id提供新值之前,假定存储的测量值是准确的;给定id的最后一次测量是序列的结束。
我有兴趣创建一个查询或视图,如果它们不存在(前一个点既不是0也不是NULL),则会在这些跨度定义的每个小时内合成新数据点,因此:< / p>
id | time | measurement
---+---------------------+-------------
1 | 2014-01-01T05:00:00 | 1.0
1 | 2014-01-01T06:00:00 | 1.0
1 | 2014-01-01T06:45:00 | 2.0
1 | 2014-01-01T07:00:00 | 2.0
1 | 2014-01-01T08:00:00 | 2.0
1 | 2014-01-01T09:00:00 | 2.0
1 | 2014-01-01T09:30:00 | 3.0
1 | 2014-01-01T10:00:00 | 3.0
1 | 2014-02-05T03:00:00 | 1.0
1 | 2014-02-05T04:00:00 | 1.0
这可行吗?
如果每个输入行都有一个&#34;持续时间&#34;是否更可行,指定其测量有效的时间量? (在这种情况下,我们将在SQL中有效地解包运行长度编码)。 [我的目标是SQL Server 2012,它具有LEAD和LAG功能,允许轻松构建]。
以SQL Server可以使用的格式提供该数据:
select id, cast(stime as datetime) as [time], measurement
from
(values
(1, '2014-01-01T05:00:00', 1.0),
(1, '2014-01-01T05:00:00', 1.0),
(1, '2014-01-01T06:45:00', 2.0),
(1, '2014-01-01T09:30:00', 3.0),
(1, '2014-01-01T11:00:00', NULL),
(1, '2014-02-05T03:00:00', 1.0),
(1, '2014-02-05T05:00:00', NULL)
) t(id, stime, measurement)
答案 0 :(得分:4)
它复杂但有效(对于你提供的数据集)
;WITH cte AS (
SELECT *
FROM (VALUES
(1, '2014-01-01T05:00:00', '1.0'),(1, '2014-01-01T06:45:00', '2.0'),
(1, '2014-01-01T09:30:00', '3.0'),(1, '2014-01-01T11:00:00', NULL),
(1, '2014-02-05T03:00:00', '1.0'),(1, '2014-02-05T05:00:00', NULL)
) as t (id, [time], measurement)
)
--Get intervals for every date
, dates AS (
SELECT MIN([time]) [min], DATEADD(hour,-1,MAX([time])) [max]
FROM cte
GROUP BY CAST([time] as date)
)
--Create table with gaps datetimes
, add_dates AS (
SELECT CAST([min] as datetime) as date_
FROM dates
UNION ALL
SELECT DATEADD(hour,1,a.date_)
FROM add_dates a
INNER JOIN dates d
ON a.date_ between d.[min] and d.[max]
WHERE a.date_ < d.[max]
)
--Get intervals of datetimes with ids and measurements
, res AS (
SELECT id,
[time],
LEAD([time],1,NULL) OVER (ORDER BY [time])as [time1],
measurement
FROM cte
)
--Final select
SELECT DISTINCT *
FROM (
SELECT r.id,
a.date_,
r.measurement
FROM add_dates a
LEFT JOIN res r
ON a.date_ between r.time and r.time1
WHERE measurement IS NOT NULL
UNION ALL
SELECT *
FROM cte
WHERE measurement IS NOT NULL
) as t
ORDER BY t.date_
输出:
id date_ measurement
1 2014-01-01 05:00:00.000 1.0
1 2014-01-01 06:00:00.000 1.0
1 2014-01-01 06:45:00.000 2.0
1 2014-01-01 07:00:00.000 2.0
1 2014-01-01 08:00:00.000 2.0
1 2014-01-01 09:00:00.000 2.0
1 2014-01-01 09:30:00.000 3.0
1 2014-01-01 10:00:00.000 3.0
1 2014-02-05 03:00:00.000 1.0
1 2014-02-05 04:00:00.000 1.0
修改强>
第一部分
如果使用dates
cte更改此部分,请执行以下操作:
, dates AS (
SELECT DATEADD(hour,DATEPART(hour,MIN([time])),CAST(CAST(MIN([time]) as date) as datetime)) [min], DATEADD(hour,-1,MAX([time])) [max]
FROM cte
GROUP BY CAST([time] as date)
)
这会截断日期中的分钟和秒值。
第二部分
在
partition by id
语句中添加LEAD
会有所不同 数据项被合并在一起
, res AS (
SELECT id,
[time],
LEAD([time],1,NULL) OVER (PARTITION BY id ORDER BY [time])as [time1],
measurement
FROM cte
)
对于原始数据集输出将是相同的。
答案 1 :(得分:2)
tal AS(SELECT -1 + ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) a(i)
CROSS JOIN (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) b(i)
CROSS JOIN (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) c(i))
<强>故障:强>
<强> 1 强>
0, 1, 2, 3, 4, 5 ..... 999
这将返回数字rnk AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY t) AS rn FROM @t)
。这是连续1小时的约41天。如果需要更大的间隔,只需添加更多交叉连接。
<强> 2 强>
id
这会将您的行排在id t m rn
1 2014-01-01 05:00:00.000 1.00 1
1 2014-01-01 06:45:00.000 2.00 2
1 2014-01-01 09:30:00.000 3.00 3
1 2014-01-01 11:00:00.000 NULL 4
1 2014-02-05 03:00:00.000 1.00 5
1 2014-02-05 05:00:00.000 NULL 6
之内并返回:
itr AS(SELECT lr.id, rr.t, DATEADD(mi, 60 - DATEPART(mi, lr.t) , lr.t) AS wt, lr.m
FROM rnk lr
LEFT JOIN rnk rr ON lr.id = rr.id AND lr.rn = rr.rn - 1
WHERE lr.m IS NOT NULL AND lr.m <> 0)
第3:强>
wt
这是主要部分。它产生间隔。 t
将保持开始时间,id t wt m
1 2014-01-01 06:45:00.000 2014-01-01 06:00:00.000 1.00
1 2014-01-01 09:30:00.000 2014-01-01 07:00:00.000 2.00
1 2014-01-01 11:00:00.000 2014-01-01 10:00:00.000 3.00
1 2014-02-05 05:00:00.000 2014-02-05 04:00:00.000 1.00
将保持间隔结束:
NULL
<强> 4 强>
最后一部分从输入表中获取所有行,过滤掉0
和{{1}}值。并且你可以通过在计数表上加入前一个时间间隔来获得另一个集合,以产生间隔中的所有小时数。
答案 2 :(得分:1)
由于我没有SQL Server环境,因此无法提供实际示例。但是,这是非常可行的。
您可以通过使用CTE连接行生成器来完成此操作。这是一个日期的行生成器: https://smehrozalam.wordpress.com/2009/06/09/t-sql-using-common-table-expressions-cte-to-generate-sequences/
类似这样的事情
With DateSequence( Date ) as
(
Select '2014-01-01T05:00:00' as Date
union all
Select dateadd(hour, 1, Date)
from DateSequence
where Date < '2014-02-05T05:00:00'
)
Select * from DateSequence option (MaxRecursion 1000)
将为您提供所需时间的表格。然后将其连接到数据表并使用分析函数获取最后一个非空值。