【问题标题】:Finding the Speed of each participant on SQL Table在 SQL 表上查找每个参与者的速度
【发布时间】:2021-07-21 07:10:19
【问题描述】:

我有一个关于几个人的动作 (people_movement) 的 SQL 表,如下所示:

Name Time Position Increment (m)
Brittany 10.20 -75
Brittany 10.30 800
Brittany 10.45 800
Brittany 11.00 900
Alan 10.15 400
Alan 10.16 -700
Alan 1020 800
Casey 9.50 800
Casey 10.01 -400
Casey 10.05 200

注意记录了时间,所以对于第一条记录,开始时间是未知的,但是以一行的时间作为下一行的开始时间。 有成千上万的参与者,行数不同,有些参与者有 4 或 5 行,但许多参与者有 100 行。 我需要计算他们的移动速度(米/分钟),所以表格如下所示:

Name Start Time End Time Position Increment (m) Increment Speed (m/min)
Brittany - 10.20 -75 -
Brittany 10.20 10.30 800 80
Brittany 10.30 11.45 800 53.33
Brittany 10.45 11.00 900 60
Alan - 10.15 400 -
Alan 10.15 10.16 -700 -700
Alan 10.16 10.20 800 200
Casey - 09.50 800 -
Casey 09.50 10.01 -400 -0.18
Casey 10.01 10.05 200 50

请注意,增量速度只是(位置增量/(开始时间 - 结束时间)),其中结束时间就是每个参与者(姓名)下一次移动的时间。

有没有办法进行这样的计算?

谢谢

【问题讨论】:

  • 如果您的 dbms 支持,您可以使用窗口函数lead/lag
  • 用您正在使用的数据库标记您的问题。

标签: python sql python-3.x performance derivative


【解决方案1】:

使用自联接。

CREATE TABLE speed_test(name varchar(10), time int, position int);
mysql> INSERT INTO speed_test VALUES ('Brittany', 1020, -75);
Query OK, 1 row affected (0,01 sec)

mysql> INSERT INTO speed_test VALUES ('Brittany', 1030, 800);
Query OK, 1 row affected (0,00 sec)

mysql> INSERT INTO speed_test VALUES ('Brittany', 1045, 800);
Query OK, 1 row affected (0,00 sec)

mysql> INSERT INTO speed_test VALUES ('Brittany', 1100, 900);
Query OK, 1 row affected (0,00 sec)

mysql> INSERT INTO speed_test VALUES ('Alan', 1015, 400);
Query OK, 1 row affected (0,00 sec)

mysql> INSERT INTO speed_test VALUES ('Alan', 1016, -700);
Query OK, 1 row affected (0,01 sec)

mysql> INSERT INTO speed_test VALUES ('Alan', 1020, 800);

   SELECT t1.name , t2.time as start_time, t1.time as end_time , 
  t1.position, t1.position/(t1.time-t2.time) 
    FROM speed_test t1 
    LEFT JOIN 
    speed_test t2
    ON t2.name=t1.name 
       AND t2.time<t1.time 
       AND t2.time=(SELECT max(time) FROM  speed_test WHERE t1.name=name AND time<t1.time);

+----------+------------+----------+----------+-------------------------------+
| name     | start_time | end_time | position | t1.position/(t1.time-t2.time) |
+----------+------------+----------+----------+-------------------------------+
| Brittany |       NULL |     1020 |      -75 |                          NULL |
| Brittany |       1020 |     1030 |      800 |                       80.0000 |
| Brittany |       1030 |     1045 |      800 |                       53.3333 |
| Brittany |       1045 |     1100 |      900 |                       16.3636 |
| Alan     |       NULL |     1015 |      400 |                          NULL |
| Alan     |       1015 |     1016 |     -700 |                     -700.0000 |
| Alan     |       1016 |     1020 |      800 |                      200.0000 |
+----------+------------+----------+----------+-------------------------------+

速度的差异是因为时间列中的 int vs time。

【讨论】:

  • 您需要将时间转换为时间格式进行计算。例如,如果 start_time 是 1045,end_time 是 1100,那么差值是 55 分钟而不是 15 分钟。它会导致计算错误(900/55 = 16.3636)但答案应该是(900/15=60)
【解决方案2】:

由于您的 TimeFormat 是 HH.mm,因此您需要分别捕获小时和分钟。

如果您使用的是SQL Server,请尝试以下操作:

Create table #temp
(
 Name varchar(100), Time numeric(6,2), Position_Increment int
)
INSERT INTO #temp
values
('Brittany',10.20,-75 )
,('Brittany',10.30,800 )
,('Brittany',10.45,800 )
,('Brittany',11.00,900 )
,('Alan',10.15,400 )
,('Alan',10.16,-700)
,('Alan',10.20,800 )
,('Casey',9.50,800 )
,('Casey',10.01,-400)
,('Casey',10.05,200 )



;with cte as 
(
      select Name
            ,Time as EndTime
            ,Position_Increment
            ,Lag(EndTimeHH,1,0) over (partition by Name order by Time ) as StartTimeHH
            ,Lag(EndTimemm,1,0) over (partition by Name order by Time ) as StartTimemm
            ,StartTime
            ,EndTimeHH
            ,EndTimemm
      from
      (
         select *
               ,SUBSTRING(cast(time as varchar),1,CHARINDEX('.',time)-1) as EndTimeHH
               ,SUBSTRING(cast(time as varchar),CHARINDEX('.',time)+1,len(time)) as EndTimemm
               ,Lag(Time,1,0) over (partition by Name order by Time ) as StartTime
         from #temp
      ) as t
)

select Name
      ,CASE WHEN StartTime =0 Then '-' ELSE CAST(StartTime AS varchar) END AS StartTime
      ,EndTime
      ,Position_Increment
      ,CASE WHEN StartTime =0 
            Then '-' 
            ELSE CAST(Position_Increment /ABS(((StartTimeHH*60+StartTimemm) - (EndTimeHH*60+EndTimemm))) AS varchar)
       END AS IncrementSpeed
from cte
order by name
 
Drop table #temp

【讨论】:

    【解决方案3】:

    下面的伪代码解释了要做什么:

    select name,
           lag(time) over (partition by name order by time) as start_time,
           time as end_time,
           ( position_increment /
             (time - lag(time) over (partition by name order by time))
           ) as increment_speed
    from t;
    

    需要注意的是,日期/时间函数因数据库而异,因此您需要使用适当的逻辑来减去两次。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2018-04-09
      • 1970-01-01
      相关资源
      最近更新 更多