英文:
SQL How to determine the duration of the action based on the current and next event?
问题
我有一个包含两列的表格:动作开始时间和动作名称。
时间 | 动作 |
---|---|
2023-07-27 04:52:00.000 | 跑步 |
2023-07-27 04:55:00.000 | 散步 |
2023-07-27 04:59:00.000 | 散步 |
2023-07-27 05:01:00.000 | 坐着 |
2023-07-27 05:06:00.000 | 散步 |
2023-07-27 05:10:00.000 | 跑步 |
我需要知道每个动作的持续时间:开始时间和结束时间。开始时间是已知的动作时间。
但是如何确定结束时间呢?也就是说,下一个动作的开始时间与当前动作不同的时间点?
以"散步"动作为例:
开始时间 | 结束时间 |
---|---|
2023-07-27 04:55:00.000 | 2023-07-27 05:01:00.000 |
2023-07-27 05:06:00.000 | 2023-07-27 05:10:00.000 |
英文:
I have a table with two columns: the action start time and the name of action.
time | action |
---|---|
2023-07-27 04:52:00.000 | running |
2023-07-27 04:55:00.000 | walking |
2023-07-27 04:59:00.000 | walking |
2023-07-27 05:01:00.000 | sitting |
2023-07-27 05:06:00.000 | walking |
2023-07-27 05:10:00.000 | running |
I need to know the duration of the action: start and end time. The start time is the time of the action, which is already known.
But how to know the end time? That is, the beginning of the next action different from the current one?
Example for the action "walking"
time_start | time_end |
---|---|
2023-07-27 04:55:00.000 | 2023-07-27 05:01:00.000 |
2023-07-27 05:06:00.000 | 2023-07-27 05:10:00.000 |
答案1
得分: 2
请参考下面的更正版本。这个版本假设每个动作都是唯一的。
我认为你可以使用lag函数来实现你的目标。
select action, time as time_start, lag(time, -1) over(order by time) as time_end from <table_name>
在这种情况下,最后一行的time_end将等于NULL,我认为这是正确的,因为它还不知道。
**编辑:**根据@Tim Biegeleisen的备注,这是基于他的工作更新的答案。然而,它正确地找到了相同连续动作的time_end值。
WITH data AS (
SELECT '2023-07-27 04:52:00.000' AS time, 'running' AS action UNION ALL
SELECT '2023-07-27 04:53:00.000', 'running' UNION ALL
SELECT '2023-07-27 04:55:00.000', 'walking' UNION ALL
SELECT '2023-07-27 04:59:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:01:00.000', 'sitting' UNION ALL
SELECT '2023-07-27 05:06:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:10:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:12:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:15:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:20:00.000', 'running'
),
withLag AS (
SELECT
action,
time AS start_time,
LAG(time) OVER (ORDER BY time desc) AS next_time,
ROW_NUMBER() OVER (ORDER BY time) AS row_num,
ROW_NUMBER() OVER (PARTITION BY action ORDER BY time) AS group_num
FROM data
)
SELECT action, min(start_time) as time_start, max(next_time) as time_end
FROM withLag
GROUP BY action, row_num - group_num
order by time_start
结果如下所示:
英文:
Please see the corrected version underneath. This one assumes that each action is unique.
> I think you could use the lag function to achieve your goal.
>
> select
> action,
> time as time_start,
> lag(time, -1) over(order by time) as time_end
> from <table_name>
>
> In this case the time_end for the last row will be equal to NULL,
> which I think is correct, as it isn't known yet.
EDIT: After the remark from @Tim Biegeleisen, this is the updated answer, built upon his work. However, it correctly finds the time_end value for groups of the same, consecutive actions.
WITH data AS (
SELECT '2023-07-27 04:52:00.000' AS time, 'running' AS action UNION ALL
SELECT '2023-07-27 04:53:00.000', 'running' UNION ALL
SELECT '2023-07-27 04:55:00.000', 'walking' UNION ALL
SELECT '2023-07-27 04:59:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:01:00.000', 'sitting' UNION ALL
SELECT '2023-07-27 05:06:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:10:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:12:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:15:00.000', 'walking' UNION ALL
SELECT '2023-07-27 05:20:00.000', 'running'
),
withLag AS (
SELECT
action,
time AS start_time,
LAG(time) OVER (ORDER BY time desc) AS next_time,
ROW_NUMBER() OVER (ORDER BY time) AS row_num,
ROW_NUMBER() OVER (PARTITION BY action ORDER BY time) AS group_num
FROM data
)
SELECT action, min(start_time) as time_start, max(next_time) as time_end
FROM withLag
GROUP BY action, row_num - group_num
order by time_start
As a result we achieve the expected outcome:
答案2
得分: 1
使用行号差异方法,我们可以尝试以下代码:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY time) rn1,
ROW_NUMBER() OVER (PARTITION BY action ORDER BY time) rn2
FROM yourTable
)
SELECT MIN(time) AS time_start,
MAX(time) AS time_end,
action
FROM cte
GROUP BY rn1 - rn2, action
ORDER BY MIN(time);
以下是来自下面演示链接的屏幕截图:
Demo
英文:
Using the difference in row numbers method we can try the following:
<!-- language: sql -->
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY time) rn1,
ROW_NUMBER() OVER (PARTITION BY action ORDER BY time) rn2
FROM yourTable
)
SELECT MIN(time) AS time_start,
MAX(time) AS time_end,
action
FROM cte
GROUP BY rn1 - rn2, action
ORDER BY MIN(time);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论