【问题标题】:SQL Server - Calculating Distance Between Two Latitude's & Longitude'sSQL Server - 计算两个纬度和经度之间的距离
【发布时间】:2023-06-02 05:56:01
【问题描述】:

使用 SQL Server 2014。

我正在尝试让 SQL Server 计算世界各地机场之间的距离,以公里、英里和海里为单位。大多数情况下,下面的方法有效,但是当距离超过 10,000Km 时它似乎会失败(尽管这只是一个假设)。

    DECLARE @Radius_Km AS FLOAT
DECLARE @Radius_Mi AS FLOAT
DECLARE @Radius_Nm AS FLOAT
DECLARE @Pi AS FLOAT
DECLARE @D2R AS FLOAT

SET @Radius_Km = '6370.97327862'
SET @Radius_Mi = '3958.73926185'
SET @Radius_Nm = '3440.05036642'
SET @Pi = '3.14159265358979'
SET @D2R = @Pi / 180;


WITH RawData AS
(
    SELECT DISTINCT Sched.AirlineName, Sched.AirlineIATA, Sched.AirlineICAO, Sched.FlightNo,

            Sched.DepartureAirportName, Sched.DepartureAirportIATA, DepAprt.AirportICAO AS [DepartureAirportICAO], 

            DepAprt.Latitude AS [DepartureAirportLatitude], DepAprt.Longitude AS [DepartureAirportLongitude],

            Sched.ArrivalAirportName, Sched.ArrivalAirportIATA, ArrAprt.AirportICAO AS [ArrivalAirportICAO], 

            ArrAprt.Latitude AS [ArrivalAirportLatitude], ArrAprt.Longitude AS [ArrivalAirportLongitude]

    FROM VAS_Live.dbo.RawData_FR24 AS Sched

        LEFT JOIN VAS_Live.dbo.ReferenceData_Airports AS DepAprt
            ON Sched.DepartureAirportIATA = DepAprt.AirportIATA

        LEFT JOIN VAS_Live.dbo.ReferenceData_Airports AS ArrAprt
            ON Sched.ArrivalAirportIATA = ArrAprt.AirportIATA

),

RadianConvert AS
(
    SELECT *,

        ((DepartureAirportLatitude / 180) * @Pi) AS [DALat],
        ((DepartureAirportLongitude / 180) * @Pi) AS [DALon],
        ((ArrivalAirportLatitude / 180) * @Pi) AS [AALat],
        ((ArrivalAirportLongitude / 180) * @Pi) AS [AALon]

    FROM RawData
),

CentralSphericalAngle AS
(
    SELECT *,

    (Sin(DALat) * Sin(AALat)) + (Cos(DALat) * Cos(AALat) * Cos(AALon - DALon)) AS [Test]

    FROM RadianConvert
),

TestCTE AS
(
    SELECT *,

        @Radius_Mi * ATAN(SQRT(1 - POWER(Test, 2)) / Test) AS [Test2]

    FROM CentralSphericalAngle
)

SELECT *
FROM TestCTE
WHERE DepartureAirportIATA = 'LHR' AND ArrivalAirportIATA = 'SIN'

对于上面的例子,LHR Lat & Lon 如下:

51.4775 -0.461389

SIN 经纬度如下:

1.35019 103.994

我已经知道距离大概是:

公里 - 10,883

小米 - 6,762

Nm - 5,876

知道为什么 SQL 给我 -5674 吗?

我曾经使用 VBA 和 Excel 来计算这些数据,这很有效。我注意到 Arrival AALat 和 DALon 在转换为弧度时计算不正确 - 但我不知道为什么。

谢谢

马修

【问题讨论】:

  • 1) 使用GEOGRAPHY 数据类型 2) 使用.STDistance 3) 避免复杂的公式、弧度/度数之间的重新计算等
  • 地理数据类型需要一个绝对年龄来计算。当我需要为数千行完成此操作时 - 恐怕这不是一个解决方案 - 除非您有任何代码您希望我专门尝试并报告回来?
  • 你在这里使用什么公式?对我来说,它看起来不像haversine。对于空间,试试这个:SELECT *, GEOGRAPHY::POINT(DepAprt.Latitude, DepAprt.Longitude, 4326).STDistance(GEOGRAPHY::Point(ArrAprt.Latitude, ArrAprt.Longitude,4326)) FROM RawData
  • 好的,试试这个,我得到 10887730.8812865 作为答案。知道我应该怎么做才能将其显示为 Km、Mi 和 Nm?
  • “地理需要一个绝对年龄来计算”。你那里有基准吗?我的经历正好相反。特别是如果您已经创建并存储了地理点。快速浏览一下您的架构,您肯定可以(如果您想保留纬度和经度,则作为计算列)。

标签: sql sql-server sql-server-2014


【解决方案1】:

从纬度和经度计算距离(英里)函数如下:

CREATE FUNCTION [dbo].[getDistanceFromLatLon](@lat1  float, @lon1 float, @lat2 float, @lon2 float)   
Returns float   
AS  
BEGIN 
 Declare  @R  int  
    set @R= 6371;   

    Declare @a float  
    Declare  @c  float  
    Declare  @d  float  

    Declare @dLat float;  
    Declare @dLon float;  
    declare @DegToRad float
BEGIN

    set @DegToRad= (select PI() / 180)
    set @dLat = (@lat2 - @lat1)* @DegToRad;   
    set @dLon = (@lon2 - @lon1)* @DegToRad;  
    set @a =   sin(@dLat / 2) * sin(@dLat / 2) +  
   cos((@lat1)* @DegToRad) * cos((@lat2)* @DegToRad) *  
    sin(@dLon / 2) * sin(@dLon / 2) ;  
    set @c = 2 * atn2(sqrt(@a), sqrt(1 - @a));  
    set @d = @R * @c;   
 END  

     return (@d * 0.6214)

END  

从这个函数,

DECLARE @Kilimiles float
DECLARE @miles float
DECLARE @nauticalmile float
SET @miles=(SELECT dbo.[getDistanceFromLatLon](51.4775, -0.461389,1.35019 ,103.994))
SET @Kilimiles=@miles * 1.60934
SET @nauticalmile= @miles * 0.868976

SELECT @miles as Mile,@Kilimiles as Kilomile,@nauticalmile as NauticalMile

【讨论】: