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

请记住,我将在lat / long对上执行计算,什么数据类型最适合与MySQL数据库一起使用?

348163 次浏览

使用MySQL的空间扩展与GIS。

FLOAT应该能给你所需的所有精度,并且比将每个坐标存储为字符串或类似的东西更适合用于比较函数。

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

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

Lat Long计算需要精度,因此使用某种类型的十进制类型,并使精度至少比您要存储的数字高2,以便执行数学计算。我不知道我的sql数据类型,但在sql server中,人们经常使用浮点数或实数而不是十进制,这就陷入了麻烦,因为这些是估计值而不是实数。所以只要确保你使用的数据类型是一个真正的十进制类型,而不是一个浮动十进制类型,你就可以了。

当我从ARINC424构建导航数据库时,我做了相当多的测试,并回顾了代码,我使用了DECIMAL(18,12)(实际上是NUMERIC(18,12),因为它是firebird)。

浮点数和双精度数没有那么精确,可能会导致舍入错误,这可能是一件非常糟糕的事情。我不记得我是否发现了任何有问题的真实数据——但我相当肯定无法准确地存储在浮点数或双精度数中可能会导致问题

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

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

MySQL的空间扩展是最好的选择,因为你有空间操作符和索引的完整列表。空间索引允许您非常快速地执行基于距离的计算。请记住,作为6.0,空间扩展仍然是不完整的。我并不是在写MySQL Spatial,只是让你在深入了解它之前知道它的缺陷。

如果你严格地处理点,而只是DISTANCE函数,这是可以的。如果需要使用polygon、Lines或Buffered-Points进行任何计算,除非使用“related”操作符,否则空间操作符不会提供准确的结果。请参阅21.5.6顶部的警告。关系,如包含,内部,或交叉使用MBR,而不是确切的几何形状(即椭圆被视为矩形)。

此外,MySQL Spatial中的距离与第一个几何图形的单位相同。这意味着如果你使用的是十进制度数,那么你的距离测量就是十进制度数。当你离赤道越来越远时,这将使你很难得到准确的结果。

我们将纬度/经度X 1,000,000作为数字存储在oracle数据库中,以避免使用双数舍入错误。

已知纬度/经度精确到小数点后第6位是10厘米,这就是我们所需要的。许多其他数据库也将lat/long存储到小数点后第6位。

PostGIS中的空间函数比MySQL中的空间函数更具功能性(即不局限于BBOX操作)。看看它:链接文本

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

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

谷歌提供了一个从开始到结束的PHP/MySQL解决方案的例子“商店定位器”应用程序与谷歌地图。在本例中,它们将lat/lng值存储为“Float”,长度为“10,6”

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

MySQL使用double为所有浮点数… 所以使用double类型。在大多数情况下,使用float将导致不可预测的四舍五入值

虽然它并不是所有操作的最佳选择,但如果你正在制作地图瓷砖或使用只有一个投影的大量标记(点)(例如Mercator,像谷歌Maps和许多其他滑头地图框架),我发现我所谓的“巨大坐标系”真的非常非常方便。基本上,你将x和y像素坐标存储在一些放大的地方——我使用缩放级别23。这有几个好处:

  • 你只需要做一次昂贵的lat/lng到墨卡托像素转换而不是每次处理点
  • 从给定缩放级别的记录中获取平铺坐标需要右移一次。
  • 从记录中获取像素坐标需要右移一次和一位and。
  • 移位是如此的轻量,以至于在SQL中执行它们是可行的,这意味着您可以执行DISTINCT来只返回每个像素位置的一条记录,这将减少后端返回的记录数量,这意味着前端的处理更少。

我在最近的一篇博客文章中谈到了这些: http://blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/ < / p >

基于这篇wiki文章 http://en.wikipedia.org/wiki/Decimal_degrees#Accuracy MySQL中适当的数据类型是Decimal(9,6),用于存储经度和纬度 单独的字段。< / p >

基本上,这取决于你需要的定位精度。使用DOUBLE可以获得3.5nm的精度。DECIMAL(8,6)/(9,6)减小到16cm。FLOAT是1.7米…

这个有趣的表有一个更完整的列表: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

希望这能有所帮助。

使用DECIMAL(8,6)表示纬度(90到-90度),使用DECIMAL(9,6)表示经度(180到-180度)。小数点后6位对于大多数应用程序都是可以的。两者都应该“有符号”以允许为负值。

这取决于你需要的精度。

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不支持索引(5.6除外,当表类型为MyISAM时)。

注意:

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

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

  • 如果你依赖谷歌来显示你的地图,标记,多边形,无论什么,那么让谷歌来完成计算!
  • 你在你的服务器上节省资源,你只是把纬度和经度存储在一起作为一个单独的字符串(VARCHAR),例如:"-0000.0000001, -0000.000000000000001"(长度为35,如果一个数字有超过7个十进制数字,那么它将被四舍五入);
  • 如果谷歌每个数字返回超过7位十进制数字,则无论如何都可以将该数据存储在字符串中,以防您想检测一些未来的逃跑或微生物;
  • 你可以使用它们的距离矩阵几何图形库来计算距离或在某些区域检测点,调用简单如下:google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
  • 有很多“服务器端”API你可以使用(在PythonRuby on RailsPHPCodeIgniterLaravelYiiZend框架等)使用谷歌地图API。

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

不需要走太远,根据谷歌地图,最好是FLOAT(10,6)的纬度和液化天然气。

博士TL;

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


要完整地回答你的问题,你需要考虑以下几点:

格式

  • __abc0: 40°26′46″n 79°58′56″w
  • __abc0: 40°26.767′n 79°58.933′w
  • __abc0: 40.446°n 79.982°w
  • __abc0: -32.60875, 21.27812
  • 其他的自制格式?没有人禁止你制作自己的以家庭为中心的坐标系统,并将其存储为标题和离家的距离。对于您正在处理的某些特定问题,这可能是有意义的。

所以答案的第一部分是-你可以将坐标存储在格式化应用程序使用的格式中,以避免来回的常量转换,并使SQL查询更简单。

大多数情况下,您使用谷歌Maps或OSM来显示数据,而gmap使用“十进制2”格式。所以用相同的格式存储坐标会更容易。

精度

然后,您需要定义所需的精度。当然,您可以存储诸如“-32.608697550570334,21.278081997935146”这样的坐标,但在导航到点时,您是否关心过毫米?如果你不是在NASA工作,也不是在研究卫星、火箭或飞机的轨迹,你应该可以接受几米的精度。

常用的格式是圆点后面加5位数字,这样可以得到50cm的精度。

例子: X,21.2780818和X,21.2780819之间有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位表示指数)就足够了。

所以在这里,我想支持阿历克斯阿克塞尔,因为评论说谷歌建议将它存储在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(不超过91000个值+符号)+ long(不超过181 000个值+符号)到21位,这是大大减少而不是2xFLOAT(8字节== 64位)

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

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

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

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

我建议您使用浮动数据类型的SQL Server。

存储Lat Long值的理想数据类型是十进制(9,6)

这是在大约10cm的精度,而只使用5字节的存储。

例如:CAST(123.456789 as decimal(9,6))

GeoLocationCoordinates返回一个双数据类型,以十进制表示位置的经纬度。你可以试着用double。