sql - 使用 SQL 对共享共同状态的连续项目进行分组(包括虚拟数据)

标签 sql sql-server datetime recursive-query gaps-and-islands

给定一个表,该表有时在组内具有重复的状态(在本例中为“车辆”),我想将这些状态合并到一行中并聚合 status_seconds。数据如下所示(我将在下面包含一些 TSQL 以将虚拟数据选择到临时表中,以便轻松使用此示例)

raw data

例如,我想将此表中车辆 T101 的前三行合并为一行,其中 status_seconds = 1+1+2(4 秒)。对于虚拟数据,这些是具有需要合并的连续状态行的车辆。

data to consolidate

请注意,在第 5-7 行中,T101 的出行状态被 T102 状态分解。

对我来说,这似乎是递归 CTE 的问题,但我发现它很难解决。

到目前为止,我已经能够识别上述的 anchor 节点。 IE。对于每辆车,我可以识别车辆状态的最后一次出现。这是虚拟数据和标识 anchor 节点的 CTE。

CREATE TABLE ##vehiclesAndStates
(
id INT,
vehicle_name VARCHAR(30),
vehicle_status VARCHAR(30),
status_end_time DATETIME, 
status_seconds INT
)
INSERT INTO ##vehiclesAndStates VALUES 
     (100, 'T101', 'STOPPED', '2020-12-04 09:43:18.000',  1)
    ,(801, 'T101', 'STOPPED', '2020-12-04 09:43:19.000', 1)
    ,(745, 'T101', 'STOPPED', '2020-12-04 09:43:20.000', 2)
    ,(925, 'T101', 'TURNING', '2020-12-04 09:43:22.000', 1)
    ,(626, 'T101', 'TRAVELLING', '2020-12-04 09:43:23.000', 10)
    ,(401, 'T102', 'STOPPED', '2020-12-04 09:43:23.000', 10)    
    ,(201, 'T101', 'TRAVELLING', '2020-12-04 09:43:33.000', 1)
    ,(808, 'T102', 'STOPPPED', '2020-12-04 09:43:33.000', 3)
    ,(707, 'T102', 'STOPPPED', '2020-12-04 09:43:35.000', 7)
    ,(888, 'T101', 'TURNING', '2020-12-04 09:43:34.000', 1)
    ,(42, 'T101', 'STOPPED', '2020-12-04 09:43:35.000', 3)
    ,(2, 'T102', 'PARKED', '2020-12-04 09:43:35.000', 10)
    ,(911, 'T101', 'TRAVELLING', '2020-12-04 09:43:35.000', 1)

SELECT * FROM ##vehiclesAndStates


-- identify anchor nodes: rows where the previous status for a vehicle was different 

;with cte_AnchorNodes as 
  (
  
SELECT i.*
  FROM (
    SELECT 
         a.ID
        ,a.vehicle_name
        ,a.vehicle_status
        ,a.status_end_time
        ,a.status_seconds   
        ,previous_vehicle_status = LAG(a.vehicle_status,1) OVER (
            ORDER BY a.vehicle_name, a.status_end_time
        )
        ,previous_ID = LAG(a.ID,1) OVER (
            ORDER BY a.vehicle_name, a.status_end_time
        )
    FROM 
        ##vehiclesAndStates a
    ) i
    WHERE i.vehicle_status <> IsNull(i.previous_vehicle_status, 'Handle Nulls')
)

结果 anchor nodes

但是,我正在努力使递归 CTE 发挥作用:

--按a.vehicle_name、a.status_end_time从cte_AnchorNodes中选择*订单

,cteRecursiveStatuses (Id, VehicleName, VehicleStatus, StatusEndTime, recursionDepth)  AS
  (
    SELECT  
             a.ID
            ,a.vehicle_name
            ,a.vehicle_status
            ,a.status_end_time
            ,0 recursionDepth 
      FROM  cte_AnchorNodes a

    UNION ALL

    SELECT 
            ?? 
    FROM 
            ##vehiclesAndStates b
    JOIN 
            cteRecursiveStatuses r ON r.Id = ??
     AND b.vehicle_status = r.VehicleStatus
  ) 

Select * From cteRecursiveStatuses

DROP TABLE ##vehiclesAndStates

最佳答案

这是一个典型的间隙和岛屿问题,您希望将共享相同车辆和状态(岛屿)的“相邻”行分组在一起。

您不需要为此进行递归查询:窗口函数可以完成此任务。在这里,最简单的方法可能是使用行号之间的差异来识别组。

select vehicle_name, vehicle_status, 
    min(status_end_time) as min_status_end_time, 
    max(status_end_time) as max_status_end_time, 
    sum(status_seconds)  as sum_status_seconds 
from (
    select vs.*, 
        row_number() over(partition by vehicle_name order by status_end_time) rn1,
        row_number() over(partition by vehicle_name, vehicle_status order by status_end_time) rn2
    from ##vehiclesAndStates vs
) t
group by vehicle_name, vehicle_status, rn1 - rn2
order by vehicle_name, min(status_end_time)

您可以单独运行子查询并查看行号如何变化以了解更多信息。

对于您的示例数据,the query returns :

vehicle_name | vehicle_status | min_status_end_time     | max_status_end_time     | sum_status_seconds
:----------- | :------------- | :---------------------- | :---------------------- | -----------------:
T101         | STOPPED        | 2020-12-04 09:43:18.000 | 2020-12-04 09:43:20.000 |                  4
T101         | TURNING        | 2020-12-04 09:43:22.000 | 2020-12-04 09:43:22.000 |                  1
T101         | TRAVELLING     | 2020-12-04 09:43:23.000 | 2020-12-04 09:43:33.000 |                 11
T101         | TURNING        | 2020-12-04 09:43:34.000 | 2020-12-04 09:43:34.000 |                  1
T101         | TRAVELLING     | 2020-12-04 09:43:35.000 | 2020-12-04 09:43:35.000 |                  1
T101         | STOPPED        | 2020-12-04 09:43:35.000 | 2020-12-04 09:43:35.000 |                  3
T102         | STOPPED        | 2020-12-04 09:43:23.000 | 2020-12-04 09:43:23.000 |                 10
T102         | STOPPPED       | 2020-12-04 09:43:33.000 | 2020-12-04 09:43:35.000 |                 10
T102         | PARKED         | 2020-12-04 09:43:35.000 | 2020-12-04 09:43:35.000 |                 10

关于sql - 使用 SQL 对共享共同状态的连续项目进行分组(包括虚拟数据),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65149199/

相关文章:

c# - 使用明细表中的外键从 C# 将数据插入 SQL Server 2005 中的主/明细表

sql-server - 条件 将相同的条件拆分到两个不同的表

c# - 从数据库中读取 SQLite DateTime 值并将其分配给 C# 字符串变量

python - 将 python 日期时间与 mysql 日期时间进行比较

SQL 主键,INT 或 GUID 或..?

sql - 如何将SQL Server几何数据插入postgis几何数据类型列

sql - 小表中列的非聚集索引

mysql - sql查找部分匹配字符串的行

MySQL 通过删除旧数据并保持行数相同来添加新数据

java - 如何从 iPhone 中的数据库中解析日期