在SQL Server中存储坐标(从Google Maps中获取的经度/纬度)的最佳方法是什么?

106

我正在设计一个 SQL Server 2008 中的表,用于存储用户列表和 Google 地图坐标(经度和纬度)。

我需要两个字段,还是只需要一个字段就可以了?

什么是存储这种数据的最佳(或最常见)数据类型?

11个回答

63

提醒!在采用GEOGRAPHY类型之前,请确保您不打算使用Linq或Entity Framework来访问数据,因为它们不受支持(截至2010年11月),否则你会感到难过!

更新于2017年7月

对于现在阅读本答案的人来说,它已经过时了,因为它是针对过时的技术堆栈。请参见评论获取更多详细信息。


1
自从原始答案发布以来,我发现了这篇文章http://www.jasonfollas.com/blog/archive/2010/02/14/spatial-data-and-the-entity-framework.aspx,讨论了一种可能的解决方法。 - Norman H
60
警告已不再适用。EF现在支持地理类型。 - Marcelo Mason
1
Nerveless EF 支持空间类型,但它使用与 WCF 数据服务不同的类型集,因此它们不兼容。 - abatishchev
1
Hibernate 5 Spatial 仍然不可用,例如 :( - Eugene
1
公平警告仍然适用于Entity Framework Core 2.0 https://github.com/aspnet/EntityFrameworkCore/issues/1100 - ono2012

57

29

我不知道SQL Server的答案,但是...

MySQL中,将其保存为FLOAT( 10, 6 )

这是来自Google开发者文档的官方建议。

CREATE TABLE `coords` (
  `lat` FLOAT( 10, 6 ) NOT NULL ,
  `lng` FLOAT( 10, 6 ) NOT NULL ,
) ENGINE = MYISAM ;

19
问题明确指出是SQL Server,而不是MySQL。而且你肯定不希望有一个只包含纬度和经度的表格像那样。 - araqnid
2
同意 - 回答不好。使用新的GEOGRAPHY空间类型。 - Pure.Krome
2
链接也已经迁移到 http://code.google.com/apis/maps/articles/phpsqlajax.html。 - Ralph Lavelle
14
因为精度问题,我不会使用float类型。建议使用decimal(9,6)类型。 - kaptan
有一些实际情况表明,在 SQL 2014 中,即使在高密度索引下,“lat”和“lng”也比“georgraphy”更有效。例如:查找所有在矩形内的点。只是我不确定,我看到 Google 地图现在使用 7 位数字而不是 6 位? - Nenad

24

我的做法是:我存储了纬度经度,然后我有第三列是根据前两列自动派生的地理类型。表格如下:

CREATE TABLE [dbo].[Geopoint]
(
    [GeopointId] BIGINT NOT NULL PRIMARY KEY IDENTITY, 
    [Latitude] float NOT NULL, 
    [Longitude] float NOT NULL, 
    [ts] ROWVERSION NOT NULL, 
    [GeographyPoint]  AS ([geography]::STGeomFromText(((('POINT('+CONVERT([varchar](20),[Longitude]))+' ')+CONVERT([varchar](20),[Latitude]))+')',(4326))) 
)

这使你可以在geoPoint列上进行空间查询,你也可以按需检索纬度和经度值以便于展示或提取为csv文件。


非常适合我所寻找的,你有关于轨道/线路的内容吗? - aggie
1
好主意,但请注意,如果有人打算这样做,您无法在计算列上创建空间索引。 - hvaughan3
1
@hvaughan3 如果你将它设置为持久化计算列,我认为你是可以的。 - NickG
@NickG 很有趣,感谢更新。我读到了一个人做类似事情的帖子,他们表现得好像它对他们起作用了,但性能非常差。不过他们可能遇到了与此略有不同的情况。 - hvaughan3
2
谢谢并点赞,你的回答帮了我很多。但是我认为使用PointSTGeomFromText更好。例如:[geography]::Point([Latitude], [Longitude], 4326) - default.kramer
显示剩余4条评论

22

对于那些说“这里有一个新类型,让我们使用它”的人,我不想成为持反对意见的人。新的SQL Server 2008空间类型有一些优点-即效率,但你不能盲目地说总是使用该类型。这实际上取决于一些更大的问题。

举个例子,集成。这种类型在.Net中有一个等效类型-但是interop呢?如何支持或扩展旧版本的.Net?如何将此类型暴露到其他平台的服务层?数据规范化怎么办-也许您只对lat或long作为独立信息感兴趣。也许您已经编写了处理长/宽的复杂业务逻辑。

我并不是说您不应该使用空间类型-在许多情况下,您应该使用。我只是说,在走这条道路之前,您应该询问一些更加关键的问题。要最准确地回答您的问题,我需要了解您特定情况的更多信息。

将long/lat分别存储或以空间类型存储均是可行的解决方案,根据您自己的情况,其中一种可能更可取。


GIS和空间数据处理具有悠久的历史,并自2000年代以来拥有标准文本和二进制表示。如果您使用空间类型和标准表示,您将遇到所有提到的问题。 - Panagiotis Kanavos

15

您需要做的是将纬度和经度存储为新的SQL2008空间类型-> GEOGRAPHY。

这是我拥有的一个表的截图。

alt text http://img20.imageshack.us/img20/6839/zipcodetable.png

在这个表中,我们有两个字段存储地理数据:

  • Boundary:这是邮政编码边界的多边形
  • CentrePoint:这是代表该多边形视觉中心点的纬度/经度点。

您希望将其保存到数据库中作为GEOGRAPHY类型的主要原因是,您可以利用所有SPATIAL方法->例如,点在Poly中,两点之间的距离等。

顺便说一下,我们还使用Google Maps API检索经纬度数据并将其存储在我们的Sql 2008 DB中--所以此方法确实有效。


1
如果你还没有使用2008版本,或者你使用的是SQLCE呢?后者不支持GEOGRAPHY类型... - fretje
3
如果 SqlCE 或者小于 2008 版本支持二进制,就可以将结果存储为 varbinary,然后在 .NET 代码中使用空间工具库 dll 对这些二进制数据进行空间计算。虽然不是最佳解决方案,但仍然是 某些 问题的可能解决方案。(可以通过 sql spatial 的 NuGet 获取该 dll)。 - Pure.Krome
唉:( 感谢Imageshack什么都没帮上忙。我已经好几年没有用IS了 :( Imgur.com才是王道! - Pure.Krome
1
抱歉,我无法回答此问题,因为您没有提供需要翻译的英文内容。 - Ilmari Karonen

11

SQL Server 支持与空间相关的信息。您可以在此处了解更多信息。

或者你可以将信息存储为两个基本字段,通常使用浮点数作为标准数据类型,大多数设备也会报告此类型,并且精度足以到达英寸或两英寸之内,对于Google Maps来说完全足够。


2

注意:这是基于最近SQL Server和.NET技术栈更新的答案

从Google Maps获取的纬度和经度应该作为Point(注意大写P)数据存储在SQL Server的地理数据类型中。

假设您当前的数据以Sample表的latlon列形式存储为varchar,以下查询将帮助您转换为地理信息。

alter table Sample add latlong geography
go
update Sample set latlong= geography::Point(lat,lon,4326)
go

PS:下一次当您对这个带有地理数据的表进行选择时,除了结果和消息选项卡之外,您还将获得如下所示的空间结果选项卡,以便进行可视化。

SSMS geo results tab


0

如果您正在使用 Entity Framework 5 <,您可以使用 DbGeography。以下是 MSDN 上的示例:

public class University  
{ 
    public int UniversityID { get; set; } 
    public string Name { get; set; } 
    public DbGeography Location { get; set; } 
}

public partial class UniversityContext : DbContext 
{ 
    public DbSet<University> Universities { get; set; } 
}

using (var context = new UniversityContext ()) 
{ 
    context.Universities.Add(new University() 
        { 
            Name = "Graphic Design Institute", 
            Location = DbGeography.FromText("POINT(-122.336106 47.605049)"), 
        }); 

    context. Universities.Add(new University() 
        { 
            Name = "School of Fine Art", 
            Location = DbGeography.FromText("POINT(-122.335197 47.646711)"), 
        }); 

    context.SaveChanges(); 

    var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)"); 

    var university = (from u in context.Universities 
                        orderby u.Location.Distance(myLocation) 
                        select u).FirstOrDefault(); 

    Console.WriteLine( 
        "The closest University to you is: {0}.", 
        university.Name); 
}

https://msdn.microsoft.com/en-us/library/hh859721(v=vs.113).aspx

我在开始使用DbGeography时遇到的问题之一是coordinateSystemId。请参见下面的答案,其中包含了对此代码的出色解释和来源。
public class GeoHelper
{
    public const int SridGoogleMaps = 4326;
    public const int SridCustomMap = 3857;

    public static DbGeography FromLatLng(double lat, double lng)
    {
        return DbGeography.PointFromText(
            "POINT("
            + lng.ToString() + " "
            + lat.ToString() + ")",
            SridGoogleMaps);
    }
}

https://dev59.com/PGUo5IYBdhLWcg3wxB0e#25563269


-4
如果你只是要将它替换到一个URL中,我想一个字段就足够了 - 这样你就可以形成一个URL,例如:
http://maps.google.co.uk/maps?q=12.345678,12.345678&z=6

但由于这是两个数据,我会将它们存储在不同的字段中。


这是我的问题。我需要将坐标存储在一个字段中,并用逗号分隔开。我认为可以使用 TEXT 作为字段类型。你觉得呢? - Amr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接