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

What is the ideal data type to use when storing latitude / longitude in a MySQL database?

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


使用MySQL的GIS空间扩展。


谷歌为使用谷歌地图的"商店定位器"应用程序提供了一个开始完成PHP / MySQL解决方案。在此示例中,它们将lat / lng值存储为"Float",长度为"10,6"

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


基本上,它取决于您所在位置所需的精度。使用DOUBLE,你将拥有3.5nm的精度。 DECIMAL(8,6)/(9,6)下降到16cm。 FLOAT是1.7米......

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

1
2
3
4
5
6
7
8
9
10
11
12
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

希望这可以帮助。


MySQL的Spatial Extensions是最佳选择,因为您可以使用空间运算符和索引的完整列表。空间索引将允许您非常快速地执行基于距离的计算。请记住,从6.0开始,Spatial Extension仍然不完整。我没有放下MySQL Spatial,只是在你对这个问题走得太远之前让你知道陷阱。

如果您严格处理积分并且只处理DISTANCE功能,那么这很好。如果需要使用"多边形","线"或"缓冲点"进行任何计算,则除非使用"关联"运算符,否则空间运算符不会提供精确结果。请参阅21.5.6顶部的警告。诸如包含,内部或相交之类的关系使用MBR,而不是精确的几何形状(即,椭圆被视为矩形)。

此外,MySQL Spatial中的距离与第一个几何体的距离相同。这意味着如果您使用十进制度数,则您的距离测量值为十进制度数。这将使你很难得到准确的结果,因为你从赤道得到了更好的结果。


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

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

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

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


取决于您需要的精度。

1
2
3
4
5
6
7
8
9
10
11
12
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)。


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


使用DECIMAL(8,6)表示纬度(90到-90度),DECIMAL(9,6)表示经度(180到-180度)。对于大多数应用程序,小数点后6位。两者都应该"签名"以允许负值。


根据谷歌地图,没有必要走远,最好的是lat和lng的FLOAT(10,6)。


我们将oracle数据库中的纬度/经度X 1,000,000存储为NUMBERS,以避免双打出现错误。

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


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

  • 如果您依靠Google来显示您的地图,标记,多边形等等,那么请让Google完成计算!
  • 您可以在服务器上保存资源,只需将纬度和经度一起存储为单个字符串(VARCHAR),例如:" - 0000.0000001,-0000.000000000000001"(长度为35,如果数字的小数位数超过7位则会得到圆形);
  • 如果谷歌每个号码返回的小数位数超过7位数,那么无论如何你都可以将这些数据存储在你的字符串中,以防你以后想要检测一些逃亡或微生物;
  • 你可以使用它们的距离矩阵或它们的几何库来计算某些区域的距离或检测点,调用就像这样简单:google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
  • 您可以使用许多"服务器端"API(使用Google Maps API,使用Python,Ruby on Rails,PHP,CodeIgniter,Laravel,Yii,Zend Framework等)。

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


取决于你的应用程序,我建议使用FLOAT(9,6)

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


虽然它并非对所有操作都是最佳的,但如果您正在制作地图图块或使用大量标记(点)只使用一个投影(例如墨卡托,如谷歌地图和许多其他滑动地图框架预期),我找到了什么我称之为"Vast Coordinate System"非常非常方便。基本上,你以某种方式存储x和y像素坐标 - 放大 - 我使用缩放级别23.这有几个好处:

  • 您只需执行一次昂贵的lat / lng到mercator像素转换,而不是每次处理该点
  • 从给定缩放级别的记录中获取切片坐标需要一次右移。
  • 从记录中获取像素坐标需要一个右移和一个按位AND。
  • 这些转换非常轻巧,在SQL中实现它们是切实可行的,这意味着您可以执行DISTINCT,每个像素位置只返回一条记录,这将减少后端返回的数字记录,这意味着更少的处理前端。

我在最近的博客文章中谈到了这一切:

Optimizing Map Tile Generation


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

为什么有人愿意自愿"预先降低"精度,然后再对更糟糕的数字进行计算呢?听起来最愚蠢。

如果源具有64位精度,那么自愿将比例缩小到例如是愚蠢的。 6位小数,并将精度限制为最多9个重要的数字(通常建议的十进制9.6格式)。

当然,可以使用源材料具有的精度来存储数据。降低精度的唯一原因是存储空间有限。

  • 以原始精度存储源图
  • 在计算发生的精度中存储从源计算的数据(例如,如果应用程序代码使用双精度数,则将结果存储为双精度数)

十进制9.6格式导致对齐网格现象。这应该是最后一步,如果它完全发生的话。

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


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


PostGIS中的空间函数比MySQL空间函数中的空间函数功能更强大(即不受BBOX操作限制)。看看:链接文字


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查询。

很可能您使用谷歌地图或OSM来显示您的数据,而GMaps使用"十进制度数2"格式。因此,以相同的格式存储坐标会更容易。

精确

然后,您想要定义所需的精度。当然你可以存储像"-32.608697550570334,21.278081997935146"这样的坐标,但你有没有关心导航到点的毫米?如果你不是在美国国家航空航天局工作而不是做卫星或火箭或飞机轨迹,你应该没有几米精度。

常用格式是点后5位数,精度为50cm。

示例:X,21.2780818和X之间的距离为1厘米,21.2780819。因此,点后7位数字给你1 / 2cm精度,点后5位数将给你1/2米精度(因为不同点之间的最小距离是1米,因此舍入误差不能超过它的一半)。对于大多数民用目的来说应该足够了。

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

节省空间的存储

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

所以在这里我想支持Alix Axel的评论说Google建议将它存储在FLOAT(10,6)中是非常多的,因为主要部分不需要4位数(因为符号是分开的,纬度是有限的到90和经度限制为180)。您可以轻松地将FLOAT(8,5)用于1 / 2m精度或FLOAT(9,6)用于50 / 2cm精度。或者您甚至可以将lat和long存储在分离的类型中,因为FLOAT(7,5)足以用于lat。请参阅MySQL float类型参考。它们中的任何一个都将像普通的FLOAT一样,无论如何都等于4个字节。

通常空间现在不是问题,但如果你想出于某种原因真的优化存储(免责声明:不做预优化),你可以压缩lat(不超过91 000值+符号)+ long(no超过181 000个值+符号)到21位,明显小于2xFLOAT(8个字节== 64位)


  • 纬度范围从-90到+90(度),所以DECIMAL(10,8)就可以了

  • 经度范围从-180到+180(度),所以你需要DECIMAL(11,8)。

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

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


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


    FLOAT应该为您提供所需的所有精度,并且比将每个坐标存储为字符串等更好地用于比较函数。

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

    Prior to MySQL 5.0.3, DECIMAL columns store values with exact precision because they are represented as strings, but calculations on DECIMAL values are done using floating-point operations. As of 5.0.3, MySQL performs DECIMAL operations with a precision of 64 decimal digits, which should solve most common inaccuracy problems when it comes to DECIMAL columns