选择使用参数选择

时间:2014-12-18 14:44:46

标签: sql sql-server

进行如下查询(为简单起见缩短并重命名)

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中实现此目的?

3 个答案:

答案 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...)

我必须对表进行限定并在内部查询中计算差异。 我必须计算内部查询的差异,所以即使这样也是可能的。