【问题标题】:zipcodes within a specific radius特定半径内的邮政编码
【发布时间】:2017-04-11 01:19:15
【问题描述】:

我正在寻找一个 SQL 查询来检索特定半径内的邮政编码。

我有一个名为 tblZip 的表,其中包含以下列:ZipcodeLat(纬度)、Long(经度)。

tblZip

Zipcode    | Lat    | Long
Short Text | Number | Number

我检查了一些其他建议使用Great-circle Distance 的答案,但我似乎不明白它是如何工作的。看到从 / 到弧度和度数的所有转换,我感到头疼,我真的很想理解它,但我的数学太差了。

感谢您的帮助和指导。

PS:我使用的是 Microsoft Access 数据库。

【问题讨论】:

  • 您将计算两条记录之间的距离还是一条记录与任意点之间的距离?如果是前者,那这两个点怎么选?
  • 我正在计算特定记录与表 tblZip 中所有其他记录之间的距离。

标签: sql ms-access latitude-longitude zipcode great-circle


【解决方案1】:

考虑在 VBA 函数中使用半正弦距离公式(借用 here)。然后在您的 SQL 查询中调用它。甚至可以根据您的需要使用WHERE 子句按距离过滤您的查询。请注意:此解决方案仅适用于 MSAccess.exe 程序。您将无法通过 ODBC/OLEDB 从外部调用它。

由于您需要比较地理编码,下面的 SQL 查询使用交叉连接,其中每条记录都与表中的所有其他记录进行比较。此外,查询避免了反向重复和相同匹配的配对,从而减少了大小。但是要小心,如果表很大,因为 self 上的交叉连接返回 N2 记录,即在过滤重复项之前。

SQL (距离为 5 公里或更短的邮政编码配对)

SELECT z1.Zipcode, z2.Zipcode, 
       GetDistance(z1.Lat, z1.Lon, z2.Lat, z2.Lon) As km_distance
FROM tblZip z1, tblZip z2
WHERE z1.Zipcode > z2.Zipcode
AND GetDistance(s1.lat, s1.lon, s1.lat, s2.lon) <= 5;

VBA (保存在独立模块中)

Function GetDistance(lat1Degrees As Double, lon1Degrees As Double, lat2Degrees As Double, lon2Degrees As Double) As Double

    Dim earthSphereRadiusKilometers As Double
    Dim kilometerConversionToMilesFactor As Double
    Dim lat1Radians As Double
    Dim lon1Radians As Double
    Dim lat2Radians As Double
    Dim lon2Radians As Double
    Dim AsinBase As Double
    Dim DerivedAsin As Double

    'Mean radius of the earth (replace with 3443.89849 to get nautical miles)
    earthSphereRadiusKilometers = 6371

    'Convert kilometers into miles (replace 0.621371 with 1 to keep in kilometers)
    kilometerConversionToMilesFactor = 0.621371

    'Convert each decimal degree to radians
    lat1Radians = (lat1Degrees / 180) * (4 * ATN(1))
    lon1Radians = (lon1Degrees / 180) * (4 * ATN(1))
    lat2Radians = (lat2Degrees / 180) * (4 * ATN(1))
    lon2Radians = (lon2Degrees / 180) * (4 * ATN(1))

    AsinBase = Sin(Sqr(Sin((lat1Radians - lat2Radians) / 2) ^ 2 + Cos(lat1Radians) * Cos(lat2Radians) * Sin((lon1Radians - lon2Radians) / 2) ^ 2))
    DerivedAsin = (AsinBase / Sqr(-AsinBase * AsinBase + 1))

    'Get distance from [lat1,lon1] to [lat2,lon2]
    GetMiles = Round(2 * DerivedAsin * (earthSphereRadiusKilometers * kilometerConversionToMilesFactor), 2)

End Function

【讨论】:

  • 非常感谢上面的详细回答。它正是我想要的。然而,计算并没有给出精确的结果。例如,我尝试了两个邮政编码“00501”和“00705”,GetMiles 函数给了我 1628.4 英里,而计算邮政编码之间距离的在线工具很少给我 1619.2 和 1617.87。您知道如何获得更精确的结果吗?
  • 可能是因为 pi 小数精度。尝试将 3.14### 常量更改为 (4 * ATN(1))。见编辑。请注意,此函数返回公里而不是英里。
  • 它没有改变任何东西。但无论如何不要介意。但是,执行此查询“SELECT TOP 100 tblZip.Zipcode, tblZip.City, tblZip.Lat, tblZip.Lon, GetDistance(40.81,-73.04,tblZip.Lat,tblZip.[Lon]) AS Distance FROM tblZip Where GetDistance(40.81 ,-73.04,tblZip.Lat,tblZip.[Lon])
  • 通过查看查询,我看不出您收到该错误的原因。确保 LatLon 是数字数据类型,而不是字符串。另外,我也编辑了最后一行,并注意到这不是存储过程,而是对 VBA 函数的 SQL 调用。
  • 感谢您指出这一点。我发现我的表中的一些记录的 Lat,Lon 是空的,而 GetMiles 是导致数据类型不匹配问题的原因,因为它正在对空变量进行所有数学运算。我试图添加一个测试 If IsNull(Lat1Degrees) Then GetMiles = 999999 else 做数学。但现在,即使有空的 Lat、Lon 记录,该测试也被评估为 False。在进行数学运算之前如何更改 GetMiles 以测试其参数中的空/空值有什么想法吗?
猜你喜欢
  • 2010-11-17
  • 2016-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
相关资源
最近更新 更多