2

我有一些看起来像这样的数据(按“时间戳”排序):

      Key     |        TimeStamp        |   Column3   |   ColumnN
--------------+-------------------------+-------------+--------------
       1      |   2012-01-01 08:00:23   |     ...     |     ...
       2      |   2012-01-01 08:01:07   |     ...     |     ...
       3      |   2012-01-01 08:02:56   |     ...     |     ...
       6      |   2012-01-01 08:02:56   |     ...     |     ...
       4      |   2012-01-01 08:03:39   |     ...     |     ...
       5      |   2012-01-01 08:04:32   |     ...     |     ...

我需要为每条记录选择“TimeStamp”的下一个不仅仅是下一行)。

例如,上面的数据如下所示:

      Key     |          Begin          |           End           |   Column3   |   ColumnN
--------------+-------------------------+-------------------------+-------------+--------------
       1      |   2012-01-01 08:00:23   |   2012-01-01 08:01:07   |     ...     |     ...
       2      |   2012-01-01 08:01:07   |   2012-01-01 08:02:56   |     ...     |     ...
       3      |   2012-01-01 08:02:56   |   2012-01-01 08:03:39   |     ...     |     ...
       6      |   2012-01-01 08:02:56   |   2012-01-01 08:03:39   |     ...     |     ...
       4      |   2012-01-01 08:03:39   |   2012-01-01 08:04:32   |     ...     |     ...
       5      |   2012-01-01 08:04:32   |   NULL                  |     ...     |     ...

我一直在尝试为此使用窗口函数,但我无法得到这个结果。有什么想法吗?

4

3 回答 3

5

甚至比 dense_rank 更酷,您可以使用lead窗口函数:

SELECT Key, ts, lead(ts) OVER(ORDER BY ts ASC)
FROM tbl;

从 Erwin 的回答中无耻地窃取了 sqlfiddle链接。

编辑:嗯,实际上它并不像你描述的那样工作因为当两个值相等时它不会选择下一个更高的值。我不会删除答案,因为我认为它在这种情况下很有用,但我会将其标记为社区 wiki。

于 2013-02-12T23:38:59.937 回答
4

这应该比相关子查询快得多

WITH x AS (
   SELECT *, dense_rank() OVER (ORDER BY ts) AS rnk
   FROM   tbl
   )
SELECT x.key, x.ts AS ts_begin, y.ts As ts_end
FROM   x
LEFT   JOIN (SELECT DISTINCT ts, rnk FROM x) y ON y.rnk = (x.rnk + 1)
ORDER  BY x.ts

-> sqlfiddle

  • 使用窗口函数dense_rank()在 CTE 中获得没有差距的排名。
  • 然后通过偏移量LEFT JOIN将结果转换DISTINCT为自身的一个版本,1以获得“下一个”时间戳(并且只有一个)。
  • 或者,您可以使用GROUP BY 1,2而不是DISTINCTfor y。我希望DISTINCT在这里更快,因为排序顺序与ORDER BY窗口函数的一致。但是请EXPLAIN ANALYZE自己检查并查看。
于 2013-02-12T23:25:04.623 回答
1

您可以像这样使用 subselect 获得“下一个”值:

SELECT "Key", 
       t1.Timestamp as "Begin", 
       (SELECT min(t2."TimeStamp") 
        FROM the_table t2
        WHERE t2."TimeStamp" > t1."TimeStamp") as "End",
       column3, ...
FROM the_table t1

但是这个查询可能很慢。如果您需要它更快 - 只需编写一个简单的 PL/SQL 函数。

于 2013-02-12T23:00:55.617 回答