7

好的,我已经搜索了 SO 和 Google,但还没有真正找到明确的答案,所以把它扔给 SO 社区。

基本上我有一个特定兴趣点的经度和纬度表,sql查询将知道您当前所在的位置(这将是表中的位置之一,作为参数传入),因此它需要计算并将与传入的最接近的纬度和经度的一行返回。

我要求这一切都在 MSSQL(2012 / 存储过程)中完成,而不是在调用应用程序(即 .NET)中完成,因为我听说 SQL 在处理此类查询时通常比 .NET 快得多?

编辑:

我找到了 STDistance 函数,它给出了位置之间的英里数,例如:

DECLARE @NWI geography, @EDI geography
SET @NWI = geography::Point(52.675833,1.282778,4326)
SET @EDI = geography::Point(55.95,-3.3725,4326)
SELECT @NWI.STDistance(@EDI) / 1000

但是我不想遍历表中的所有纬度/经度,因为这对性能肯定会很糟糕吗?

我还尝试转换以下评论链接之一中指出的示例之一(这是 MYSQL 而不是 MSSQL)但是我收到错误,代码如下:

DECLARE @orig_lat decimal(6,2), @orig_long decimal(6,2), @bounding_distance int
set @orig_lat=52.056736; 
set @orig_long=1.14822; 
set @bounding_distance=1;

SELECT *,((ACOS(SIN(@orig_lat * PI() / 180) * SIN('lat' * PI() / 180) + COS(@orig_lat * PI() / 180) * COS('lat' * PI() / 180) * COS((@orig_long - 'lon') * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS 'distance' 
FROM [DB1].[dbo].[LatLons]
WHERE
(
  'lat' BETWEEN (@orig_lat - @bounding_distance) AND (@orig_lat + @bounding_distance)
  AND 'lon' BETWEEN (@orig_long - @bounding_distance) AND (@orig_long + @bounding_distance)
)
ORDER BY 'distance' ASC

收到的错误是:

消息 8114,级别 16,状态 5,第 6 行将数据类型 varchar 转换为数字时出错。

任何人都能够解决上述代码或提出更好的解决方案?

4

5 回答 5

11

让我们看一个STDistance在 SQL Server 2008(及更高版本)中使用该函数的简单示例。

我要告诉 SQL Server 我在伦敦,我想看看我的每个办公室离我有多远。这是我希望 SQL Server 给我的结果:

办公室

首先,我们需要一些样本数据。我们将创建一个包含一些 Microsoft 办事处位置的表,并将它们的经度和纬度值存储在一个geography字段中。

CREATE TABLE [Offices] (
    [Office_Id] [int] IDENTITY(1, 1) NOT NULL,
    [Office_Name] [nvarchar](200) NOT NULL,
    [Office_Location] [geography] NOT NULL,
    [Update_By] nvarchar(30) NULL,
    [Update_Time] [datetime]
) ON [PRIMARY]

GO

INSERT INTO [dbo].[Offices] VALUES ('Microsoft Zurich', 'POINT(8.590847 47.408860 )', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft San Francisco', 'POINT(-122.403697 37.792062 )', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Paris', 'POINT(2.265509 48.833946)', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Sydney', 'POINT(151.138378 -33.796572)', 'mike', GetDate())
INSERT INTO [dbo].[Offices] VALUES ('Microsoft Dubai', 'POINT(55.286282 25.228850)', 'mike', GetDate())

现在,假设我们在伦敦。以下是如何geography从伦敦的经度和纬度值中获取值:

DECLARE 
    @latitude numeric(12, 7),
    @longitude numeric(12, 7)

SET @latitude = 51.507351
SET @longitude = -0.127758

DECLARE @g geography = 'POINT(' + cast(@longitude as nvarchar) + ' ' + cast(@latitude as nvarchar) + ')';

最后,让我们看看我们每个办公室的距离。

SELECT [Office_Name], 
       cast([Office_Location].STDistance(@g) / 1609.344 as numeric(10, 1)) as 'Distance (in miles)' 
FROM [Offices]
ORDER BY 2 ASC

这给了我们希望的结果。

结果

显然,TOP(1)如果你只是想看看最近的办公室,你可以溜进去。

前 1 个结果

酷,嘿?

只有一个障碍。当您有很多geography要比较的点时,性能并不出色,即使您在该数据库字段上添加了空间索引。

我在 330,000 点的表格中测试了一个geography点。使用此处显示的代码,它在大约8 秒内找到了最近的点。

当我修改我的表以存储经度和纬度值并使用此 StackOverflow 文章[dbo].[fnCalcDistanceMiles]中的函数时,它在大约3 秒内找到了最近的点。

然而...

我在互联网上找到的所有“两点之间的距离”示例要么使用 SQL ServerSTDistance函数,要么使用涉及(CPU 密集型)cos、sin 和 tan 函数的数学公式。

一个更快的解决方案是回到高中,记住毕达哥拉斯是如何计算两点之间的距离的。

假设我们想知道伦敦和巴黎之间的距离。

在此处输入图像描述

这是我的 SQL Server 函数:

CREATE FUNCTION [dbo].[uf_CalculateDistance] (@Lat1 decimal(8,4), @Long1 decimal(8,4), @Lat2 decimal(8,4), @Long2 decimal(8,4))
RETURNS decimal (8,4) AS
BEGIN
    DECLARE @d decimal(28,10)

    SET @d = sqrt(square(@Lat1-@Lat2) + square(@Long1-@Long2))

    RETURN @d
END

现在,请记住此函数不会返回以英里、公里等为单位的值……它只是比较经度和纬度值。毕达哥拉斯是用于二维的,而不是比较圆形星球上的点!

但是,在我的测试中,它在1 秒内找到了最近的点,并产生了与使用 SQL ServerSTDistance函数相同的结果。

因此,请随意使用此功能比较相对距离,但如果您需要实际距离本身,请不要使用此功能。

希望这一切都有帮助。

于 2016-05-19T14:20:55.610 回答
5

几年前我写了一篇博客,解释了如何在不使用空间数据类型的情况下做到这一点。由于您似乎有一张经度/纬度值表,因此此博客可能会有所帮助。

SQL Server 邮政编码纬度经度邻近搜索

从 Archive.org 保存的同一页面

于 2013-07-09T16:03:06.823 回答
2

在我的表格中,我有 5 个字段:[名称]、[时间]、[描述]、[纬度]、[经度]。在这里,用户可以传递他/她想要结果的当前位置和距离。

declare @latitude float, @longitude float, @distance float 
select @latitude = 28.6493, @longitude = 77.2217 , @distance = 5
;with RestroomLocationsWithDistance as
    (
        select
        [Name]   ,[Timings]     ,[Description],   
        ( 3959 * acos( cos( radians(@latitude) ) * cos( radians( [Lattitude] ) ) * cos( radians( [Longitude] )
       - radians(@longitude) ) + sin( radians(@latitude) ) * sin( radians( [Lattitude] ) ) ) )  As Distance            
        FROM [dbo].[Locations]
    )
    select
    [Name]   ,[Timings]     ,[Description],    Distance        
    from RestroomLocationsWithDistance
    where Distance <= @distance
于 2015-08-27T10:09:42.917 回答
0

由于这是 SQL Server,只需在表的 Lat/Lon 列上放置一个空间索引,然后使用.STDistance()WHERE 子句中的函数对其进行查询。

Microsoft 文档中的这篇文章解释了如何做到这一点:http: //msdn.microsoft.com/en-us/library/ff929109.aspx。第二部分“最近邻查询.. ”似乎特别相关。

于 2013-07-09T15:48:18.437 回答
0

这里我是如何使用上面 Mike 的函数的:

    SELECT TOP 1 [State], [City], [AddressID], sqrt(square(abs(la.Latitude-@lat)) + square(abs(la.Longitude-@lng))) as Distance
    FROM [LocationAddresses] la     
    ORDER BY Distance
于 2016-05-27T02:15:31.997 回答