进行如下查询(为简单起见缩短并重命名)
SELECT
SOME_COLUMN AS value1,
SOME_COMMON_ID as commonID,
SOME_TAG as tagID,
SOME_TIMESTAMP as endTime,
( SELECT
SOME_TIMESTAMP AS beginTime
FROM
EVENTLIST
WHERE
EVENTLIST.SOME_TAG = 'BEGIN'
AND EVENTLIST.SOME_COMMON_ID = commonID /* <-- Invalid column name commonID */
),
endTime - beginTime AS duration
FROM
EVENTLIST
JOIN
(...some irrelevant lookups on other tables)
WHERE
(...some criteria...)
我想要实现的目标:
此表记录了一些事件,事件发生的时间存储在SOME_TIMESTAMP
中。多个事件按名为SOME_COMMON_ID
的公共标识符分组。事件的类型存储在SOME_TAG
对于每个事件,我想选择自事件标记为BEGIN
如何在SQL Server中实现此目的?
答案 0 :(得分:1)
让我们创建一些测试数据
DECLARE @EventList TABLE
(
SOME_COLUMN_ID int,
SOME_COLUMN varchar(20),
SOME_TAG varchar(20),
SOME_TIMESTAMP datetime
)
INSERT INTO @EventList
( SOME_COLUMN_ID, SOME_COLUMN, SOME_TAG, SOME_TIMESTAMP )
VALUES
( 1, 'Exporting', 'BEGIN', DATEADD(HOUR, -5, GETDATE()) ),
( 1, 'Exporting', 'GOING', DATEADD(HOUR, -4, GETDATE()) ),
( 1, 'Exporting', 'STILL_GOING', DATEADD(HOUR, -3, GETDATE()) ),
( 1, 'Exporting', 'GONE', DATEADD(HOUR, -2, GETDATE()) ),
( 1, 'Exporting', 'END', DATEADD(HOUR, -1, GETDATE()) ),
( 2, 'Parsing1', 'BEGIN', DATEADD(HOUR, -5, GETDATE()) ),
( 2, 'Parsing2', 'GOING', DATEADD(HOUR, -4, GETDATE()) ),
( 2, 'Parsing3', 'STILL_GOING', DATEADD(HOUR, -3, GETDATE()) ),
( 2, 'Parsing4', 'GONE', DATEADD(HOUR, -2, GETDATE()) );
现在我要做一个CTE按时间排序事件并按ID分区
WITH T AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY SOME_COLUMN_ID ORDER BY SOME_TIMESTAMP) RN
FROM @EventList
)
现在我们将完成所有事件的发现并获取每个步骤的持续时间,我也检查过程是否达到END,否则我现在用时间来查找持续时间。
SELECT
T1.SOME_COLUMN_ID,
T1.SOME_COLUMN,
T1.SOME_TAG,
T1.SOME_TIMESTAMP AS BeginTime,
(CASE WHEN t1.SOME_TAG != 'END' THEN ISNULL(t2.SOME_TIMESTAMP, GETDATE()) ELSE NULL END) EndTime,
(CASE WHEN t1.SOME_TAG != 'END' THEN DATEDIFF(MINUTE, t1.SOME_TIMESTAMP, ISNULL(t2.SOME_TIMESTAMP, GETDATE())) ELSE NULL END) Duration
FROM T t1
LEFT JOIN T t2
ON t1.SOME_COLUMN_ID = t2.SOME_COLUMN_ID
AND t1.RN = t2.RN - 1
这是输出:
SOME_COLUMN_ID SOME_COLUMN SOME_TAG BeginTime EndTime Duration
1 Exporting BEGIN 2014-12-18 05:31:06.090 2014-12-18 06:31:06.090 60
1 Exporting GOING 2014-12-18 06:31:06.090 2014-12-18 07:31:06.090 60
1 Exporting STILL_GOING 2014-12-18 07:31:06.090 2014-12-18 08:31:06.090 60
1 Exporting GONE 2014-12-18 08:31:06.090 2014-12-18 09:31:06.090 60
1 Exporting END 2014-12-18 09:31:06.090 NULL NULL
2 Parsing1 BEGIN 2014-12-18 05:31:06.090 2014-12-18 06:31:06.090 60
2 Parsing2 GOING 2014-12-18 06:31:06.090 2014-12-18 07:31:06.090 60
2 Parsing3 STILL_GOING 2014-12-18 07:31:06.090 2014-12-18 08:31:06.090 60
2 Parsing4 GONE 2014-12-18 08:31:06.090 2014-12-18 10:31:06.090 120
答案 1 :(得分:0)
如果您更改子选择以将BEGIN
时间转换为JOIN
,则可以更轻松地解决列命名问题,并可能获得一些性能。
使用当前模式,时间戳计算需要在更高级别的查询嵌套中进行,因为分配给subselect值和常规表值的别名不能在同一查询的SELECT
内使用水平。这也通过使用JOIN
来解决。
SELECT
curevt.SOME_COLUMN AS value1,
curevt.SOME_COMMON_ID as commonID,
curevt.SOME_TAG as tagID,
curevt.SOME_TIMESTAMP as endTime,
beginevt.SOME_TIMESTAMP AS beginTime,
-- In the JOIN scenario, the calc can happen here, without using the alias
curevt.SOME_TIMESTAMP - beginevt.SOME_TIMESTAMP AS duration
FROM
-- The table first for current events
EVENTLIST curevt
-- Join against itself for the BEGIN events
INNER JOIN EVENTLIST beginevt
-- Join condition on SOME_COMMON_ID and also the BEGIN event
ON curevt.SOME_COMMON_ID = beginevt.SOME_COMMON_ID
AND beginevt.SOME_TAG = 'BEGIN'
以下是一个示例,使用DATEDIFF()
查找与datetime
列的分钟差异。 http://sqlfiddle.com/#!6/b02ac/7,虽然听起来你可能有类似Unix时间戳的东西。相应地进行计算,概念是相同的。
注意:如果您的某些SOME_COMMON_ID
值没有相应的BEGIN
事件,则需要在此使用LEFT JOIN
而不是INNER JOIN
来确保它们显示在结果集中。您可能还需要使用duration
或类似的方式略微处理IFNULL()
计算,因为它将是NULL
。
答案 2 :(得分:0)
这比我想象的要容易得多。
SELECT
SOME_COLUMN AS value1,
SOME_COMMON_ID as commonID,
SOME_TAG as tagID,
SOME_TIMESTAMP as endTime,
( SELECT
DATEDIFF(SECOND, e2.SOME_TIMESTAMP, e1.SOME_TIMESTAMP) AS duration
/* ^-- calc the diff here, not in the outer query */
FROM
EVENTLIST e2
WHERE
e2.SOME_TAG = 'BEGIN'
AND e2.SOME_COMMON_ID = e1.SOME_COMMON_ID /* <-- qualify table names */
)
FROM
EVENTLIST e1 /* <-- name required */
JOIN
(...some irrelevant lookups on other tables)
WHERE
(...some criteria...)
我必须对表进行限定并在内部查询中计算差异。 我必须计算内部查询的差异,所以即使这样也是可能的。