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


103

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

我需要两个字段,还是可以用1来完成?

用于存储此类数据的最佳(或最常见)数据类型是什么?

Answers:



63

公平警告!在接受有关使用GEOGRAPHY类型的建议之前,请确保您不打算使用Linq或Entity Framework访问数据,因为该数据不受支持(截至2010年11月),您会为此感到遗憾!

2017年7月更新

对于那些现在阅读此答案的人来说,它已过时,因为它指的是落后的技术堆栈。请参阅评论以获取更多详细信息。


1
自发布原始答案以来,我发现本文jasonfollas.com/blog/archive/2010/02/14/…正在讨论一种可能的解决方法。
诺曼·H

56
警告不再有效。EF现在支持地理类型。
马塞洛·梅森

1
Nerveless EF确实支持空间类型,它使用与WCF数据服务不同的类型集,因此不兼容
abatishchev 2012年

1
休眠5空间仍然不是例如:(
尤金

1
公平警告仍适用Entity Framework Core 2.0 github.com/aspnet/EntityFrameworkCore/issues/1100
ono2012

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


14
由于精度问题,我不会使用float。使用十进制(9,6)。
kaptan 2012年

在实际情况下,where latlng性能georgraphy甚至优于SQL 2014中的高密度索引。例如:查找所有点都带有矩形。只是我不确定,我知道Google地图现在使用7而不是6位数吗?
Nenad 2015年

22

这样做的方式:存储纬度经度,然后有第三列,这是第一列和第二列的自动派生的地理类型。该表如下所示:

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。


伟大的东西我一直在寻找,你有没有音轨/线东西
阿吉

根据您的情况,另一种可行的方法是存储经纬度,然后在运行时动态地动态创建地理对象。
Zapnologica

1
好主意,但要注意,如果有人这样做,则不能在计算列上创建空间索引。
hvaughan3'6

1
@ hvaughan3我想如果您将其设为持久的计算列,则可以。
NickG

2
感谢和+1,您的回答对我有所帮助。但我认为最好使用Point代替STGeomFromText。例如:[geography]::Point([Latitude], [Longitude], 4326)
default.kramer

21

我讨厌对那些说“这是一种新类型,让我们使用它”的人表示反对。新的SQL Server 2008空间类型具有一些优点-即效率,但是您不能盲目地说总是使用该类型。这实际上取决于更大的图片问题。

例如,集成。这种类型在.Net中具有等价类型-但是互操作又如何呢?如何支持或扩展旧版本的.Net?将这种类型跨服务层暴露给其他平台呢?关于数据的规范化-也许您对lat或长期以来的独立信息感兴趣。也许您已经编写了复杂的业务逻辑来处理经/纬度。

我并不是说您不应该使用空间类型-在许多情况下应该使用。我只是说你在走那条路之前应该问一些更关键的问题。为了使我能够最准确地回答您的问题,我需要更多地了解您的具体情况。

分别存储长/经度或以空间类型存储都是可行的解决方案,根据您自己的情况,一个可能优于另一个。


GIS和空间数据处理历史悠久,至少从2000年代开始就有标准的文本,二进制表示形式。如果您使用空间类型和标准表示,那么您将遇到所有提到的问题
Panagiotis Kanavos

15

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

这是我的桌子的屏幕截图。

替代文字http://img20.imageshack.us/img20/6839/zipcodetable.png

在此表中,我们有两个用于存储地理数据的字段。

  • 边界:这是作为邮政编码边界的多边形
  • CentrePoint:这是纬度/经度点,代表此多边形的视觉中间点。

要将其以GEOGRAPHY类型保存到数据库的主要原因是,这样您就可以利用所有的SPATIAL方法了->例如。多边形中的点,两点之间的距离等

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


1
如果您还没有使用2008,或者使用SQLCE,该怎么办?后者不支持GEOGRAPHY类型...
fretje

3
如果SqlCE或<2008支持二进制,则可以将结果存储为varbinary,然后使用Spatial工具库dll对.NET代码中的二进制数据表示形式进行空间计算。不是最好的解决方案,但仍然是某些问题的可能解决方案。(为sql space ..获取该dll的Nuget)。
Pure.Krome

2
图片链接已损坏
Bryan Denny

urgh :(感谢您什么都没有imageshack。多年来我一直没有使用过IS :( imgur.com一直以来!
Pure.Krome

1
-1,没有图像,此答案不完整。请考虑将其替换为新图像或文本表说明,或删除此答案。
Ilmari Karonen


2

注意:这是基于最新的SQL Server,.NET堆栈更新的最新答案。

Google Maps的纬度和经度应作为Point(注释大写P)数据存储在SQL服务器中的地理数据类型下。

假设您当前的数据Sample以varchar的形式存储在表中的列latlon下方,则查询将帮助您转换为地理位置

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

PS:下次当您在此表上使用地理数据进行选择时,除了“结果和消息”选项卡之外,您还将获得“空间结果”选项卡,如下图所示

SSMS地理位置结果标签


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/zh-CN/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://stackoverflow.com/a/25563269/3850405


-4

如果您只是将其替换为URL,我想可以使用一个字段-这样您就可以形成如下URL

http://maps.google.co.uk/maps?q=12.345678,12.345678&z=6

但由于是两段数据,我会将它们存储在单独的字段中


这是我的情况。我只需要将坐标存储在一个字段中,并用逗号分隔即可。我认为可以使用TEXT作为字段类型。你怎么看?
Amr

-10

将它们存储为float并在其上使用唯一的关键字。i.em

create table coordinates(
coord_uid counter primary key,
latitude float,
longitude float,
constraint la_long unique(latitude, longitude)
);

为了确保只有一组唯一的纬度和经度对。您不想在表中存储两次坐标{0,0},不是吗?
重力1998年

1
您可能根本不需要像这样的单独的坐标表,尤其是在具有唯一性约束的情况下,这是一个维护噩梦,它处理两个位置引用同一点的情况,更不用说清理未引用的行了。
araqnid

2
> 您可能根本不想拥有这样的单独的坐标表 -根本不?决不?您如何将其存储在1个字段中?数百万不使用SQL 2008 Spatial类型的人呢?
莎莉2010年

2
具有这样的约束是一个错误的决定。假设鲍勃住在爱丽丝曾经住过的房子,House A并将搬到他House B的家。很快,鲍勃(Bob)将无法保存他的地址(位置),因为爱丽丝(Alice)尚未更新她的地址(或永远不会更新)。
jweyrich

@Sally-他不是这个意思。阅读他的评论。他说,应该没有理由在一对单独的表中存储一对值。只需将纬度/经度放在原始表上,并节省第二个表和所有JOINS的开销。
NickG
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.