请记住,我将在纬度/经度对上执行计算,哪种数据类型最适合与 MySQL 数据库一起使用?
21 回答
将 MySQL 的空间扩展与 GIS 结合使用。
基本上,这取决于您的位置所需的精度。使用 DOUBLE,您将获得 3.5nm 的精度。DECIMAL(8,6)/(9,6) 下降到 16 厘米。FLOAT 为 1.7m...
这个非常有趣的表有一个更完整的列表: 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
希望这可以帮助。
Google 为带有 Google Maps 的示例“Store Locator”应用程序提供了一个从头到尾的 PHP/MySQL 解决方案。在此示例中,它们将 lat/lng 值存储为“Float”,长度为“10,6”
MySQL 的 Spatial Extensions 是最佳选择,因为您可以使用空间运算符和索引的完整列表。空间索引将允许您非常快速地执行基于距离的计算。请记住,从 6.0 开始,空间扩展仍然不完整。我不是在贬低 MySQL Spatial,只是让你在你在这方面走得太远之前知道其中的陷阱。
如果您严格处理点并且只处理 DISTANCE 函数,这很好。如果您需要使用多边形、线或缓冲点进行任何计算,除非您使用“相关”运算符,否则空间运算符不会提供准确的结果。请参阅21.5.6顶部的警告。包含、内部或相交等关系使用的是 MBR,而不是确切的几何形状(即,椭圆被视为矩形)。
此外,MySQL Spatial 中的距离与您的第一个几何图形的单位相同。这意味着如果您使用十进制度,那么您的距离测量值是十进制度。当您远离赤道时,这将很难获得准确的结果。
当我为从 ARINC424 构建的导航数据库执行此操作时,我进行了大量测试并回顾代码,我使用了 DECIMAL(18,12)(实际上是 NUMERIC(18,12),因为它是 firebird)。
浮点数和双精度数不那么精确,可能会导致舍入错误,这可能是一件非常糟糕的事情。我不记得我是否发现任何有问题的真实数据 - 但我相当肯定无法准确存储在浮点数或双精度数中可能会导致问题
关键是,当使用度数或弧度时,我们知道值的范围——小数部分需要最多的数字。
MySQL Spatial Extensions是一个不错的选择,因为它们遵循OpenGIS Geometry Model。我没有使用它们,因为我需要保持我的数据库可移植。
取决于您需要的精度。
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 个十进制数字,则四舍五入); - 如果 Google 为每个数字返回超过 7 个十进制数字,则无论如何您都可以将该数据存储在您的字符串中,以防万一您想在将来检测到一些逃跑或微生物;
- 您可以使用他们的距离矩阵或几何库来计算距离或检测某些区域中的点,调用如下所示:
google.maps.geometry.poly.containsLocation(latLng, bermudaTrianglePolygon))
- 您可以使用大量使用 Google Maps API 的“服务器端”API(在Python、Ruby on Rails、PHP、CodeIgniter、Laravel、Yii、Zend Framework等中)。
这样,您就不必担心索引编号以及与可能破坏坐标的数据类型相关的所有其他问题。
TL;博士
如果您不在 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/2 厘米的精度,而点后的 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(no超过 181 000 个值 + 符号)到 21 位,明显小于2xFLOAT(8 字节 == 64 位)
虽然它不是所有操作的最佳选择,但如果您正在制作地图图块或使用只有一个投影的大量标记(点)(例如墨卡托,如谷歌地图和许多其他滑动地图框架所期望的),我发现了什么我称“大坐标系”非常非常方便。基本上,您以某种方式放大存储 x 和 y 像素坐标——我使用缩放级别 23。这有几个好处:
- 您对墨卡托像素进行一次昂贵的 lat/lng 转换,而不是每次处理该点时
- 从给定缩放级别的记录中获取平铺坐标需要右移一次。
- 从记录中获取像素坐标需要一次右移和一次按位与。
- 移位非常轻量级,可以在 SQL 中执行,这意味着您可以执行 DISTINCT 以每个像素位置仅返回一条记录,这将减少后端返回的记录数,这意味着更少的处理前端。
我在最近的一篇博文中谈到了这一切:http: //blog.webfoot.com/2013/03/12/optimizing-map-tile-generation/
根据您的应用程序,我建议使用 FLOAT(9,6)
空间键将为您提供更多功能,但在生产基准测试中,浮点数比空间键快得多。(平均 0,01 VS 0,001)
MySQL 对所有浮点数使用双精度...所以使用双精度类型。在大多数情况下,使用浮点数会导致不可预测的舍入值
纬度范围从 -90 到 +90(度),因此 DECIMAL(10, 8) 可以
经度范围从 -180 到 +180(度),因此您需要 DECIMAL(11, 8)。
注意:第一个数字是存储的总位数,第二个是小数点后的数字。
简而言之:lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL
PostGIS 中的空间函数比 MySQL 空间函数中的函数更实用(即不受 BBOX 操作的限制)。看看:链接文本
我建议您对 SQL Server 使用 Float 数据类型。
存储 Lat Long 值的理想数据类型是 decimal(9,6)
这是大约 10 厘米的精度,同时仅使用 5 个字节的存储空间。
例如 CAST(123.456789 作为十进制 (9,6))
Lat Long 计算需要精度,因此请使用某种类型的小数类型并使精度至少比您将存储的数字高 2 以执行数学计算。我不知道我的 sql 数据类型,但在 SQL Server 中,人们经常使用浮点数或实数而不是十进制数并遇到麻烦,因为这些是估计数字而不是真实数字。所以只要确保你使用的数据类型是真正的十进制类型而不是浮点十进制类型,你应该没问题。