根据SQL中的连续标志对行进行分组(Redshift)

时间:2020-07-01 08:22:35

标签: sql amazon-redshift

我有一个棘手的问题,我想在这里解决,但到目前为止我仍无法解决。

所以问题是这样的:我有跟踪数据,随着时间的推移会产生记录。假设您有一个机器人在四处行驶,并且每秒记录一次它的位置。这些位置中的每个位置都记录为数据库中的一个记录(我们使用AWS Redshift)。

每条记录都有一个tracking_id,它在属于同一跟踪源的所有记录中是唯一的,即对于机械手是唯一的。然后,我有一个在全球范围内唯一的record_id,一个时间戳和一个标志,该标志指示记录是在机器人位于定义区域之内还是之外时创建的。然后还有一些其他数据,例如坐标。

这是一个小例子。粉色框是区域,绿线是机器人的路径,蓝点是生成的记录。 Example path

因此,现在我想基于区域标志对记录进行分组(请看下面的屏幕截图)。所以我想将区域内的子路径隔离到一条记录中,并获取开始和结束时间戳记和位置。这些ID无关紧要,因此即使我将ID列出在期望的结果中,我也不必保持跟踪或记录ID。

Input and desired output

感谢您的帮助,我将不胜感激!同样,仅解决部分问题,例如如何基于标志进行分组而不在子路径中获取第一个和最后一个值也将有所帮助。

2 个答案:

答案 0 :(得分:0)

这是一个空白和孤岛的问题。在这种情况下,您希望in_zone恰好是TRUE的岛屿(其中有两个)。我们可以在此处使用行数差异法:

WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY tracking_id ORDER BY timestamp) rn1,
        ROW_NUMBER() OVER (PARTITION BY tracking_id, in_zone ORDER BY timestamp) rn2
    FROM yourTable
)

SELECT
    tracking_id,
    MIN(record_id) AS record_id,
    MIN(timestamp) AS start_timestamp,
    MAX(timestamp) AS end_timestamp,
    (SELECT t2.coordinates FROM yourTable t2
     WHERE t2.record_id = MIN(t1.record_id) AND t2.tracking_id = t1.tracking_id) AS entry_coordinates,
    (SELECT t2.coordinates FROM yourTable t2
     WHERE t2.record_id = MAX(t1.record_id) AND t2.tracking_id = t1.tracking_id) AS exit_coordinates
FROM cte t1
WHERE
    in_zone = 'TRUE'
GROUP BY
    tracking_id,
    rn1 - rn2,
    in_zone
ORDER BY
    tracking_id,
    record_id DESC;

screen capture from demo link below

Demo

答案 1 :(得分:0)

这是一个空白问题。我将使用LAG()来处理它,以识别先前的组内和累积总和。您还可以使用条件聚合来获取第一个和最后一个坐标值:

SELECT tracking_id, MIN(record_id), MIN(timestamp) as start_timestamp,
       MIN(timestamp) as end_timestamp,
       MAX(CASE WHEN prev_in_zone IS NULL OR prev_in_zone <> in_zone THEN coordinates END) as entry_coordinates,
       MAX(CASE WHEN next_in_zone IS NULL OR next_in_zone <> in_zone THEN coordinates END) as entry_coordinates
FROM (SELECT t.*,
             SUM( CASE WHEN prev_in_zone = in_zone THEN 0 ELSE 1 END) OVER (PARTITION BY tracking_id ORDER BY timestamp ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as grp
      FROM (SELECT t.*,
                   LAG(in_zone) OVER (PARTITION BY tracking_id ORDER BY timestamp) as prev_in_zone,
                   LEAD(in_zone) OVER (PARTITION BY tracking_id ORDER BY timestamp) as next_in_zone
            FROM t
           ) t
     ) t
WHERE in_zone = 'TRUE'
GROUP BY tracking_id, grp;

非常感谢蒂姆,here是db <>小提琴。