【问题标题】:SQL: performing a calculation and ensuring the first record of each id is a null valueSQL:执行计算并确保每个id的第一条记录为空值
【发布时间】:2014-03-12 07:24:26
【问题描述】:

我使用的是 SQL Server 2012。我有一个非常大的数据库,其中包含超过 4000 万条用于邮件传递的 GPS 轨迹记录。我可以使用以下查询计算每条记录的时间差:

UPDATE a SET SecSince=
  DATEDIFF(SECOND, (SELECT TOP 1 [DateTime]
                 FROM [dbo].[Database]
                 WHERE
                     COMPANY=a.COMPANY
                     AND CarrierID=a.CarrierID
                     AND  [DateTime]< a.[DateTime]
                 ORDER BY [DateTime] DESC)
              , a.[DateTime])
FROM [dbo].[Database] a
WHERE 0 < (SELECT COUNT(*) FROM [dbo].[Database]
           WHERE
               Company=a.Company
               AND CarrierID=a.CarrierID
               AND [DateTime] < a.[DateTime])

结果如下:

Company CarrierID   SecSince    
    A   1           NULL     
    A   1           800    
    A   1           900    
    B   1           NULL       
    B   1           876    
    B   1           904    
    B   2           NULL       
    B   2           122    
    B   2           177    

每个新的运营商 ID 和公司的第一条记录是一个 NULL 值——这正是我想要的。

这就是我遇到困难的地方。我想运行相同的查询,但这次我想计算 GPS 距离。我可以使用这个公式计算Haversine距离:

UPDATE A
set A.[Haversine] = 
6371000* 2 * ASIN ( SQRT (POWER(SIN((A.Latitude - B.Latitude)*pi()/180 / 2),2) + COS(A.Latitude * pi()/180) * COS(B.Latitude *pi()/180) * POWER(SIN((A.Longitude - B.Longitude) *pi()/180 / 2), 2) ) )
from [dbo].[Database] A INNER JOIN [dbo].[Database] B ON B.ID = (A.ID + 1)
WHERE A.[Trip] = A.[Trip]

这在距离计算方面效果很好。除了现在我想要每个新 CarrierID 和公司组合记录(或旅行)的第一条记录的 NULL 值,就像在 SecSince 计算中一样。

我已尝试重新编写 SecSince 查询,以便它运行距离计算,但我似乎无法获得正确的语法。我想问题出在嵌套的 select top 1 上。我希望有人能告诉我我做错了什么并帮助我使用正确的语法。

UPDATE A
set A.[Haversine] = 
6371000* 2 * ASIN ( SQRT (POWER(SIN((A.Latitude - B.Latitude)*pi()/180 / 2),2) + COS(A.Latitude * pi()/180) * COS(B.Latitude *pi()/180) * POWER(SIN((A.Longitude - B.Longitude) *pi()/180 / 2), 2) ) )
(SELECT TOP 1 [DateTime]
        FROM [dbo].[Database]
                 WHERE
                     COMPANY=a.COMPANY
                     AND CarrierID=a.CarrierID
                     AND  [DateTime]< a.[DateTime]
                 ORDER BY [DateTime] DESC)
              , a.[DateTime])
from [dbo].[Emerson] A INNER JOIN [dbo].[Database] B ON B.ID = (A.ID + 1)
WHERE A.[Trip] = A.[Trip]
FROM [dbo].[Database] a
WHERE 0 < (SELECT COUNT(*) FROM [dbo].[Database]
           WHERE
               Company=a.Company
               AND CarrierID=a.CarrierID
               AND [DateTime] < a.[DateTime])

这是我想要的结果:

Trip Company CarrierID Latitude Longitude ID    DateTime           SecSince Haversine DESIRED
    3   7002    1   50.06417    -96.46426   1   15/02/2013 14:40:38 NULL    6.05    NULL
    3   7002    1   50.06421    -96.4643    2   15/02/2013 14:55:37 899     0.00    6.05
    3   7002    1   50.06421    -96.4643    3   15/02/2013 15:08:46 789     16.61   0.00
    4   7003    1   50.06435    -96.46421   4   15/02/2013 15:21:06 NULL    0.00    NULL
    4   7003    1   50.06435    -96.46421   5   15/02/2013 15:22:16 70      0.00    0.00
    4   7003    1   50.06435    -96.46421   6   15/02/2013 15:23:06 50      0.00    0.00
    5   7002    3   50.06435    -96.46421   7   15/02/2013 15:32:51 NULL    0.00    NULL
    5   7002    3   50.06435    -96.46421   8   15/02/2013 15:33:01 10      5.51    0.00
    5   7002    3   50.0644     -96.46421   9   15/02/2013 15:38:30 329     5.51    5.51
    5   7002    3   50.06435    -96.46421   10  15/02/2013 15:45:04 394     0.00    5.51

【问题讨论】:

    标签: sql sql-server gps


    【解决方案1】:

    如果您可以使用 SQL Server 2012,您应该使用很酷的新功能,例如在这种情况下 LAG

    with cte as
    (
      select 
        id,
        lag([datetime],1) over(partition by trip order by id) as prev_datetime,
        lag([latitude],1) over(partition by trip order by id) as prev_latitude,
        lag([longitude],1) over(partition by trip order by id) as prev_longitude
      from 
        data
    )
    update data
    set
      secsince = DATEDIFF(SECOND, prev_datetime, [datetime]),
      haversine = 6371000* 2 * ASIN ( SQRT (POWER(SIN((Latitude - prev_Latitude)*pi()/180 / 2),2) + COS(Latitude * pi()/180) * COS(prev_Latitude *pi()/180) * POWER(SIN((Longitude - prev_Longitude) *pi()/180 / 2), 2) ) )
    from 
      data
      inner join cte on cte.id = data.id
    

    查看SQLFiddle

    【讨论】:

    • 非常感谢。这正是我想要的,查询运行只需要 5 分钟。它工作得很好,而且比我以前做的更有效率。
    猜你喜欢
    • 2022-07-11
    • 1970-01-01
    • 2020-08-27
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    相关资源
    最近更新 更多