将纬度/经度存储在MySQL数据库中时,理想的数据类型是什么?


431

请记住,我将在经纬度对上执行计算,哪种数据类型最适合与MySQL数据库一起使用?


1
我发现此链接非常有用:howto-use-mysql-spatial-ext.blogspot.com/2007/11/…它可能有些旧,但其中包含示例的完整说明。
madc 2011年

恕我直言,这里的大多数人都不知道会发生什么。只要应用程式码接触到一个数字(只要使用双精度字符)(大多数情况下这样做),数字最多变成双精度。然后将其存储为一百万个小数将不会有任何好处。用有限的小数位数(例如6)存储它会破坏部分精度,并且每次将其重新写入数据库时​​都会增加累积的错误。一个双精度数带有大约16个有效数字,可能全是小数。删除其中的10个会随着时间的推移而产生累积的错误。原因是“浮点数”。续
暴风城

续:当存储从外部来源获取的,未更改且首次的数字作为原始资料时,可以使用6位小数。但是,即使对它执行一次计算并再次存储,通过强制使用特定的十进制格式来删除部分精度也是愚蠢的。仅在服务器内部执行计算可能会有所不同(服务器可能会或可能不会在内部使用除double之外的其他功能),并且在app计算中使用比double差的数字表示形式会降低对存储精度的需求。
暴风城

续:如果服务器以更高的精度存储数字,尽管声明为“ 9.6”(我不知道是否确实如此),那么所有这些都不重要,格式纯粹是为了方便-几乎没有处理精度问题。但是,如果服务器使用该格式将任意数字四舍五入为小数点后六位精度,我不会感到惊讶。
暴风城

续:最后:对于经纬度,小数点后第六位是捕捉到ca 的问题。11厘米网格。每次读取(触摸),再次计算和存储时(用6位小数表示),都会有新的捕捉(=累积错误)。如果所有错误碰巧都朝着同一方向发展,那将是一个错误。如果对其执行临时乘法(例如放大,然后减去然后缩小),则它可能会更大。没有良好的放光度,不要放弃精度!
暴风城

Answers:


161

在GIS中使用MySQL的空间扩展


25
您是否有其他链接到示例或其他有关如何最好地开始使用它们的信息?
Codebeef

6
MYSQL Spatial是一个不错的选择,但是仍然有很多限制和警告(截至6)。请在下面看到我的答案...
James Schek

1
@詹姆斯·谢克(James Schek)是对的。另外,MySQL使用欧几里得几何进行所有计算,因此它并不代表lat / lng的实际用例。
mkuech

仅供参考;Mysql仅通过* .myisam表(即ISAM引擎)支持空间索引。链接:dev.mysql.com/doc/refman/5.0/en/creating-spatial-indexes.html
PodTech.io

在更新的最后部分看一下这篇文章:mysqlserverteam.com/mysql-5-7-and-gis-an-example
Jaspal Singh

149

Google为使用Google Maps的示例“ Store Locator”应用程序提供了一个完成PHP / MySQL解决方案的起点。在此示例中,他们将经度/经度值存储为“浮点数”,长度为“ 10,6”

http://code.google.com/apis/maps/articles/phpsqlsearch.html


11
Google显然不了解FLOAT规范的工作原理:FLOAT(10,6)在坐标的整数部分保留4位数字。不,符号不计数-来自(un)signed属性。
Alix Axel

2
但是,如果您需要将[0,180]中的值作为整数部分存储,那就足够了吧?
Hrvoje Golcic 2014年

37
@AlixAxel我认为Google知道自己在做什么。因为它指出:“ 使用Google Maps的当前缩放功能,您只需要在小数点后6位数字的精度。这将使字段在小数点后6位数字存储,再加上在小数点前最多4位数字,例如- 123.456789度。 ”。如果选中unsigned,则模式为1234,567890。所以没问题。
2014年

16
@AlixAxel他正在计算序列中的数字;没有使用实际的坐标...
Andrew Ellis

8
Double为Laravel 使用数据类型
FooBar 2014年

133

基本上,这取决于您所需的位置精度。使用DOUBLE,您将拥有3.5nm的精度。DECIMAL(8,6)/(9,6)下降到16cm。浮标为170万...

这个非常有趣的表具有更完整的列表:http : //mysql.rjweb.org/doc.php/latlng

Datatype               Bytes            Resolution

Deg*100 (SMALLINT)     4      1570 m    1.0 mi  Cities
DECIMAL(4,2)/(5,2)     5      1570 m    1.0 mi  Cities
SMALLINT scaled        4       682 m    0.4 mi  Cities
Deg*10000 (MEDIUMINT)  6        16 m     52 ft  Houses/Businesses
DECIMAL(6,4)/(7,4)     7        16 m     52 ft  Houses/Businesses
MEDIUMINT scaled       6       2.7 m    8.8 ft
FLOAT                  8       1.7 m    5.6 ft
DECIMAL(8,6)/(9,6)     9        16cm    1/2 ft  Friends in a mall
Deg*10000000 (INT)     8        16mm    5/8 in  Marbles
DOUBLE                16       3.5nm     ...    Fleas on a dog

希望这可以帮助。


2
我需要针对帖子的内容写一篇建设性的详细评论,所以我要说的是,在观察Rick James网站提供的准确度表的同时,我对解决方案描述“狗跳蚤”感到很高兴,并且感到值得荣誉。从技术上讲,这是一个有用的描述,它帮助我决定了在存储用于测量两个地址之间距离的坐标时要使用哪种数据类型,@ Simon,感谢您的分享。
Sam_Butler,2017年

FWIW,该链接对“ SMALLINT缩放”的使用效率非常低。Oguzhan的答案是在4字节带符号的int中存储小数点后7位数字的long / lat的好方法。尺寸小(4B),精度高(〜1cm)。
ToolmakerSteve

74

MySQL的Spatial Extensions是最佳选择,因为您可以使用完整的空间运算符和索引列表。空间索引使您可以非常快速地执行基于距离的计算。请记住,从6.0版开始,空间扩展仍不完整。我并没有放弃MySQL Spatial,只是让您知道了陷阱,然后再进行深入的探讨。

如果您只处理点,而仅处理DISTANCE函数,那很好。如果需要使用多边形,直线或缓冲点进行任何计算,除非您使用“关系”运算符,否则空间运算符不会提供确切的结果。请参阅21.5.6顶部的警告。诸如包含,内部或相交之类的关系使用的是MBR,而不是确切的几何形状(即,椭圆被视为矩形)。

另外,MySQL Spatial中的距离与第一个几何图形的单位相同。这意味着,如果您使用的是小数度,则距离的测量单位为小数度。当您从赤道上走远时,这将很难获得准确的结果。


26
重申:MySQL Spatial Extensions不适合计算经纬度表示的地球表面上各点之间的大圆距离。它们的距离函数等仅在笛卡尔,平面,坐标上有用。
O. Jones

71

当我对使用ARINC424构建的导航数据库进行此操作时,我进行了大量测试并回顾了代码,我使用了DECIMAL(18,12)(实际上是NUMERIC(18,12),因为它是火鸟)。

浮点数和双精度数不够精确,可能会导致舍入错误,这可能是非常糟糕的事情。我不记得我是否发现任何有问题的真实数据-但我很确定不能正确存储浮点数或双精度数会导致问题

关键是,使用度或弧度时,我们知道值的范围-小数部分需要最多的数字。

MySQL中的空间扩展是一个很好的选择,因为他们遵循的开放GIS几何模型。我之所以没有使用它们,是因为我需要保持数据库的可移植性。


3
谢谢,这很有帮助。读到2008年的所有这些问题和答案感到很奇怪,因为意识到它已经是8年前了。
aexl

1
@TheSexiestManinJamaica-在IEEE 754-1985之前,计算机浮点硬件非常混乱。在机器上甚至a*b有不相等的b*a值(对于某些值)。有许多示例,例如:2+2 = 3.9999。该标准消除了很多麻烦,几乎所有硬件和软件都“迅速”采用了该标准。因此,这种讨论不仅是自2008年以来就是有效的,而且已经持续了十三年。
瑞克·詹姆斯

42

取决于所需的精度。

Datatype           Bytes       resolution
------------------ -----  --------------------------------
Deg*100 (SMALLINT)     4  1570 m    1.0 mi  Cities
DECIMAL(4,2)/(5,2)     5  1570 m    1.0 mi  Cities
SMALLINT scaled        4   682 m    0.4 mi  Cities
Deg*10000 (MEDIUMINT)  6    16 m     52 ft  Houses/Businesses
DECIMAL(6,4)/(7,4)     7    16 m     52 ft  Houses/Businesses
MEDIUMINT scaled       6   2.7 m    8.8 ft
FLOAT                  8   1.7 m    5.6 ft
DECIMAL(8,6)/(9,6)     9    16cm    1/2 ft  Friends in a mall
Deg*10000000 (INT)     8    16mm    5/8 in  Marbles
DOUBLE                16   3.5nm     ...    Fleas on a dog

来自:http : //mysql.rjweb.org/doc.php/latlng

总结一下:

  • 最精确的可用选项是DOUBLE
  • 最常见的使用类型是DECIMAL(8,6)/(9,6)

MySQL 5.7开始,请考虑使用空间数据类型(SDT),专门POINT用于存储单个坐标。在5.7之前的版本中,SDT不支持索引(表类型为MyISAM时为5.6)。

注意:

  • 使用POINT类时,用于存储坐标的参数的顺序必须为POINT(latitude, longitude)
  • 创建空间索引有一种特殊的语法。
  • 使用SDT的最大好处是您可以访问空间分析功能,例如,计算两个点之间的距离(ST_Distance)并确定一个点是否包含在另一区域内(ST_Contains)。

2
您复制了先前答案的粘贴部分并“摘要”了创建该表的人所不建议的内容:«如何分区?好吧,MySQL非常挑剔。因此FLOAT / DOUBLE都消失了。DECIMAL结束了。因此,我们陷入了困境。本质上,我们需要将Lat / Lng转换为INT的某个大小,并使用PARTITION BY RANGE。» AND«FLOAT有24个有效位;DOUBLE共有53个。(它们不与PARTITIONing一起使用,但是为了完整起见而包含在内。通常,人们在使用DOUBLE时并没有意识到它有多少矫kill过正,以及占用了多少空间。)»只需保留您编写的SDT部分即可。
Armfoot

1
@Armfoot如果您查看编辑时,这是从我那里复制的另一个答案。但这并不重要:我看到Stack Overflow更多地是“给我未来的笔记”。
Gajus 2015年

1
不,他没有从您这里复制,他只是像在2014年引用的链接中粘贴表格一样粘贴了您的表格(您的帖子来自2015年)。顺便说一句,我认为您在链接空间数据类型时拼写为“ Special” 。您写的这部分内容实际上对于想要开始使用它们的人很有用,如果您像James所提到的那样添加更多示例,CREATE TABLE geom (g GEOMETRY NOT NULL, SPATIAL INDEX(g)) ENGINE=MyISAM;以及有关SDT限制的警告,也许您的答案也将更简洁,更准确地帮助其他人。 ..
Armfoot

@Gajus-我很荣幸你们两个找到了我的文件!(不,我不知道跳蚤有多大,但我觉得它会引起人们的注意。)
瑞克·詹姆斯

使用POINT类时,用于存储坐标的参数的顺序必须为POINT(经度/ X,纬度/ Y)。
AndreyP


19

使用DECIMAL(8,6)纬度(90至-90度)和DECIMAL(9,6)经度(180〜-180度)。对于大多数应用程序,小数点后6位是可以的。两者都应进行“签名”以允许使用负值。


DECIMAL类型用于不floor/ceil接受的财务计算。普通FLOAT股明显优于大市DECIMAL
Kondybas '17

1
@Kondybas-由于数据库的主要成本是获取行,因此浮点数和十进制数之间的性能差异不值得关注。
瑞克·詹姆斯

14

根据Google Maps的说法,无需走太远,最好的纬度和经度是FLOAT(10,6)。


您在哪里找到了我找不到的信息?以防万一有什么变化。
webfacer

1
@webfacer,位于此处的“在MySQL中创建表”部分中:developers.google.com/maps/documentation/javascript/…例如 lat FLOAT( 10, 6 ) NOT NULL, lng FLOAT( 10, 6 ) NOT NULL
turrican_34,19年

1
@webfacer,似乎从开始FLOAT不赞成使用该语法mysql 8.0.17。Mysql现在建议仅使用FLOAT不带任何精确参数的dev.mysql.com/doc/refman/8.0/en/numeric-type-overview.htmldev.mysql.com/doc/refman/5.5/en/floating-point- types.html
turrican_34

7

我们在oracle数据库中将纬度/经度X 1,000,000存储为NUMBERS,以避免四舍五入的错误。

假设到小数点后第六位的经度/纬度为10厘米,这是我们所需要的。许多其他数据库也存储纬度/经度到小数点后第六位。


2
如果您有很多数据,那么乘以一个很大的数字(例如一百万)是很好的,因为整数运算(例如索引检索)比浮点数快得多。
Kaitlin Duck Sherwood

@KaitlinDuckSherwood-位为位-我不知道32位浮点数(索引或其他方式)检索比32位整数慢的任何原因。这些天,即使是浮动数学运算也足够快以至于无法解决。不过,我同意注释使用带整数的隐式乘法器:它将最大程度地提高您从32位中获得的精度。随着技术的进步,一些面向未来的方法。
ToolmakerSteve

6

从完全不同和更简单的角度来看:

  • 如果您依靠Google来显示地图,标记,多边形等等,那么让Google来进行计算!
  • 您可以在服务器上节省资源,只需将纬度和经度一起存储为单个字符串(VARCHAR),例如:“ -0000.0000001,-0000.000000000000001 ”(长度为35,如果一个数字的位数超过7个,则四舍五入)。
  • 如果Google每个数字返回的位数超过7个十进制数字,则无论如何,您都可以将数据存储在字符串中,以防万一您将来想要检测某些飞沫或微生物
  • 您可以使用它们的距离矩阵几何库通过以下简单的调用来计算距离或检测某些区域中的点google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
  • 您可以使用许多使用Google Maps API 的“服务器端” API(在PythonRuby on RailsPHPCodeIgniterLaravelYiiZend Framework等中)。

这样,您就不必担心索引编号以及与数据类型相关联的所有其他问题,这些问题可能会破坏您的坐标。


不好。OP表示他将对经纬度对进行计算-您的回答排除了这一点
Yarin

4
@Yarin这是一个非常普遍的问题,其中一些(或很多)人只需要回答有关如何根据自己的需要存储坐标的答案(其中很多人可能仅使用Google地图)。您的不赞成意见表示此答案可能对他们没有帮助...通过将坐标存储在字符串中,他们将确切知道提供给他们的原始值(例如,由Google提供),如果他们决定发展自己的坐标,这将在以后对他们有所帮助拥有自己的应用并对其进行计算。那时,他们仍然拥有原始的原始数据,只是因为他们没有将其与转换搞混。
Armfoot '16

4

根据您的应用程序,我建议使用FLOAT(9,6)

空间键将为您提供更多功能,但是按生产基准测试,浮标比空间键快得多。(AVG中的0,01 VS 0,001)


1
您可以在此处提供测试结果的详细信息吗?
NameNotFoundException

4

MySQL对所有浮点数都使用double。因此,请使用double类型。在大多数情况下,使用浮点数将导致不可预测的舍入值


1
MySQL 在中执行操作DOUBLE。MySQL使您可以数据存储为4字节FLOAT或8字节DOUBLE。因此,将表达式存储到FLOAT列中时可能会失去精度。
瑞克·詹姆斯

4

虽然并非所有操作都最佳,但是如果您仅使用一个投影就制作地图图块或使用大量标记(点)(例如Mercator,例如Google Maps和许多其他滑动地图框架所期望的),我发现了什么我称“大型坐标系”非常方便。基本上,您以某种方式存储x和y像素坐标-我使用的是缩放级别23。这有几个好处:

  • 您只需执行一次昂贵的经/纬向墨卡托像素变换,而不是每次处理该点
  • 在给定缩放级别的情况下,从记录中获取平铺坐标需要向右移动一位。
  • 从记录中获取像素坐标需要一个右移和一个按位与。
  • 这些移位非常轻巧,以至于可以在SQL中进行操作,这意味着您可以执行DISTINCT来返回每个像素位置仅一条记录,这将减少后端返回的记录数,这意味着对前端。

我在最近的博客文章中谈到了所有这些问题:http : //blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/


4

我对某些答案/评论感到非常惊讶。

到底为什么有人愿意自愿“降低”精度,然后在以后对更差的数字进行计算呢?听起来最终是愚蠢的。

如果源具有64位精度,则自愿将比例固定为例如,这肯定是愚蠢的。6位小数,并将精度限制为最多9位有效位数(通常建议的9.6位十进制格式会发生这种情况)。

自然地,人们以原始资料所具有的精度来存储数据。降低精度的唯一原因将是有限的存储空间。

  • 以原始精度存储源数据
  • 以精确的精度存储从源计算得出的数字(例如,如果应用代码使用双精度,则将结果存储为双精度)

十进制9.6格式会导致捕捉到网格现象。如果这真的发生的话,那应该是最后一步。

我不会邀请累积的错误来我的巢。


2
因为大多数GPS工具和应用程序只能精确到小数点后6位。毫无意义地以比gis.stackexchange.com/questions/8650/…
Yarin

1
@Yarin是的,但是您谈论的是测量和GPS,问题中未提及。无疑,存在更准确的数字。但让我们考虑一下GPS;假设一个64位浮点数的源数据集已经包含了不准确性。小数点后6位表示将纬度捕捉到最接近的11厘米。因此,通过现在仅存储数据(带小数点后6位),您可能会出现22厘米的潜在误差(如果原来也是11厘米)。在存储第3次之前,可能会自愿执行64位计算-现在33 cm的误差窗口为+ -16 cm。听起来很蠢,恕我直言。
暴风城

@Rick James我可能会将其存储为64位,即。0.3333333333333333。我们谈论地理数据,对不对?“ 1/3”在自然界中通常不会以合理的精度进行测量的情况下很少出现。
暴风城

4

TL; DR

如果您不是在NASA /军方工作,也不在制造飞机的导航系统,请使用FLOAT(8,5)。


要完全回答您的问题,您需要考虑以下几点:

格式

  • 度分秒:40°26′46″ N 79°58′56″ W
  • 小数点的分钟数:40°26.767′N 79°58.933′W
  • 十进制度1:40.446°N 79.982°W
  • 十进制2:-32.60875,21.27812
  • 其他一些自制格式?没有人禁止您创建自己的以居家为中心的坐标系,并将其存储为航向和离家的距离。对于您正在处理的某些特定问题,这可能很有意义。

因此,答案的第一部分将是-您可以将坐标以应用程序使用格式存储,以避免来回不断的转换并简化SQL查询。

您最有可能使用Google Maps或OSM来显示数据,而GMaps使用的是“十进制2”格式。因此,以相同格式存储坐标会更加容易。

精确

然后,您想定义所需的精度。当然,您可以存储“ -32.608697550570334,21.278081997935146”之类的坐标,但是在导航到该点时您是否曾经关心过毫米?如果您不是在NASA工作,也不在做卫星,火箭或飞机的轨迹,则精度应达到几米。

常用的格式是点后5位数,可达到50厘米的精度。

示例:X,21.278081 8和X,21.278081 9之间有1cm的距离。因此,点后7位数字可为您提供1 / 2cm的精度,点后5位数字可为您提供1/2米的精度(因为不同点之间的最小距离为1m,因此舍入误差不能超过其一半)。对于大多数民用目的,它就足够了。

度十进制分钟数格式(40°26.767′N 79°58.933′W)为您提供与点后5位完全相同的精度

节省空间的存储

如果您选择了十进制格式,则您的坐标是一对(-32.60875,21.27812)。显然,2 x(1位用于符号,2位用于度数,5位用于指数)就足够了。

所以在这里,我想从评论中支持 Alix Axel,说谷歌建议将它存储在FLOAT(10,6)中确实是多余的,因为主体不需要4位数字(因为符号是分隔的,并且纬度是有限的到90,经度限制为180)。您可以轻松地将FLOAT(8,5)用于1 / 2m精度,或者将FLOAT(9,6)用于50 / 2cm精度。或者甚至可以将lat和long存储为单独的类型,因为FLOAT(7,5)对于lat就足够了。参见MySQL浮点类型参考。它们中的任何一个都将像普通的FLOAT一样,无论如何都等于4个字节。

通常,如今空间不再是问题,但是如果出于某些原因要真正优化存储(免责声明:请勿进行预优化),则可以压缩lat(不超过91 000个值+符号)+ long(不大于181 000的值+符号)到21位,明显小于 2xFLOAT(8字节== 64位)


3

与MySQL空间函数相比,PostGIS中的空间函数具有更多的功能(即不受限于BBOX操作)。检查一下:链接文本


1
  1. 纬度范围是-90到+90(度),因此DECIMAL(10,8)可以

  2. 经度的范围是-180到+180(度),因此您需要DECIMAL(11,8)。

注意:第一个数字是存储的总位数,第二个数字是小数点后的数字。

简而言之: lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL



-2

Lat Long计算需要精度,因此请使用某种类型的十进制类型,并使精度至少比要存储的数字高2以便执行数学计算。我不知道我的sql数据类型,但是在SQL Server中,人们经常使用float或real而不是小数,并且遇到麻烦,因为这些是估计数字,不是真实数字。因此,只需确保使用的数据类型是真正的十进制类型,而不是浮点十进制类型,就可以了。


1
浮点型和十进制类型都有其位置。根据经验,浮点数表示物理变量,而小数表示可数的实体(主要是货币)。我不明白为什么您更喜欢十进制的经纬度
哈维尔(Javier)

1
我也认为浮点数适合经纬度。至少在SQL Server上(4字节7位)。
DragoljubĆurčić09年

浮标不准确,估计很长一段时间内的准确率是致命的!它可能会指出您在地球上完全不同的位置。
HLGEM,2009年

2
浮点数据类型的最大错误足够低,这不应该成为问题。我的意思是,无论如何,您都必须意识到两种实现的错误乘法/累加。
Spidey 2012年

@HLGEM-舍入到小数点后的位数也会使您位于地球上的另一个位置。问题是那个不同的地点是否如此之近以至于没有关系。
瑞克·詹姆斯

-3

A FLOAT应该为您提供所需的所有精度,并且比起将每个坐标存储为字符串等,它对于比较函数而言要更好。

如果您的MySQL版本低于5.0.3,则可能需要注意某些浮点比较错误

在MySQL 5.0.3之前,DECIMAL列以精确的精度存储值,因为它们以字符串表示,但是DECIMAL值的计算是使用浮点运算完成的。从5.0.3开始,MySQL以64位十进制数字的精度执行DECIMAL操作,这应该可以解决DECIMAL列中最常见的不准确问题。


2
您需要一个真正的纬度/经度坐标数据类型以便于数学运算。想象一下像“从距离(stores.location,mylocation)<5英里的商店中选择* *”的等效项一样的便利
Kirk Strauser

1
以前从未听说过空间扩展,听起来确实很方便,以前必须在继承的应用程序上进行很多地理相关的计算,所以必须将其检出。
ConroyP

@ConroyP-否。该引用指出DECIMAL由于使用浮动实现,在5.0.3之前存在某些错误。
瑞克·詹姆斯
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.