ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我发现这个链接非常有用:howto-use-mysql-spatial-ext.blogspot.com/2007/11/… 它可能有点旧,但它包含一个完整的解释,包括示例。
恕我直言,这里的大多数人都不明白会发生什么。只要应用程序代码接触到一个数字,只要使用双精度数(大多数情况下),该数字最多会变成双精度。然后用一百万个小数存储它不会有任何好处。以有限的小数位数(例如 6 位)存储它会破坏该精度的一部分,并在每次将其重新写入数据库时添加一个累积的错误。 double 携带 ca 16 个有效数字,可能是所有小数。随着时间的推移,废弃其中的 10 个会产生累积的错误。出于某种原因,它是“浮点”。续。
续:当存储从外部来源获取的、未更改的和第一次作为源材料的数字时,6 位小数可能是可以的。但是,如果对它执行一次计算并再次存储它,那么通过强制执行特定的十进制格式来删除它的部分精度是愚蠢的。仅在服务器内部执行计算可能会有所不同(服务器可能会或可能不会在内部使用除 doubles 之外的其他东西),并且在应用程序计算中使用比 double 更差的数字表示 c 会同样减少对存储精度的需求。
继续:如果服务器以更高的精度存储数字,尽管声称“9.6”(我不知道是否如此),那么所有这些都不重要,格式纯粹是为了方便 - 几乎没有处理精度问题。但是,如果服务器实际上使用该格式将任何数字四舍五入为 6 位小数精度,我不会感到惊讶。
续:最后:对于纬度,经度,小数点后 6 位是转换为 ca 的问题。 11 厘米网格。每次读取(触摸)、计算和存储时,使用 6 位小数,将有一个新的捕捉(= 累积错误)。如果所有的错误都发生在同一个方向上,就会出现很大的错误。如果对其执行临时乘法(例如,放大,然后减去和缩小),它可能会变得更大。不要在没有好的理由的情况下废弃精度!

S
Simon

基本上,这取决于您的位置所需的精度。使用 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

希望这可以帮助。


我需要针对帖子的内容写一篇有建设性的详细评论,所以我会说,在观察 Rick James 网站提供的准确度表时,我对分辨率描述“狗身上的跳蚤”和觉得值得称赞。从技术上讲,这是一个有用的描述,它帮助我决定在存储坐标以测量两个地址之间的距离时使用什么数据类型,@Simon,我想感谢你的分享。
FWIW,该链接对“SMALLINT scaled”的使用效率非常低。 Oguzhan's answer 是在 4 字节有符号整数中存储小数点后 7 位的 long/lat 的好方法。小尺寸 (4B) 中的高精度 (~1cm)。
字节列是否准确? MySQL referenceDOUBLE 是 8 个字节。
在这里回答我自己的问题,我猜他们已经将每种类型的字节数加倍以考虑纬度+经度(即2 doubles == 16 bytes)。
K
Kirk Strauser

将 MySQL 的 spatial extensions 与 GIS 一起使用。


MYSQL Spatial 是一个不错的选择,但仍然有很大的限制和警告(从 6 开始)。请看下面我的回答...
@James Schek 是对的。另外,MySQL 使用欧几里得几何进行所有计算,因此它并不代表 lat/lng 的实际用例。
供参考; Mysql 仅支持 *.myisam 表的空间索引,即 ISAM 引擎。链接:dev.mysql.com/doc/refman/5.0/en/creating-spatial-indexes.html
看看这篇文章到底更新部分:mysqlserverteam.com/mysql-5-7-and-gis-an-example
这个答案怎么会在没有例子的情况下得到这么多单行答案的投票!
T
Ted Avery

Google 为带有 Google Maps 的示例“Store Locator”应用程序提供了一个从头到尾的 PHP/MySQL 解决方案。在此示例中,它们将 lat/lng 值存储为“Float”,长度为“10,6”

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


Google 显然不了解 FLOAT 规范的工作原理:FLOAT(10,6) 为坐标的整数部分留下 4 位数字。不,符号不算数 - 它来自 (un)signed 属性。
但是,如果您需要将 [0, 180] 中的整数部分值存储为足够的值,对吗?
@AlixAxel 我认为谷歌知道它在做什么。因为它声明:“使用 Google 地图当前的缩放功能,您应该只需要小数点后 6 位精度。这将使字段存储小数点后 6 位,再加上小数点前最多 4 位,例如 - 123.456789 度。”。如果选中 unsigned,则模式将为 1234,567890。所以没有问题。
@AlixAxel 他正在数序列中的数字;不使用实际坐标...
为 Laravel 使用数据类型 Double
J
James Schek

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

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

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


重申:MySQL 空间扩展不适用于计算地球表面上由纬度/经度表示的点之间的大圆距离。它们的距离函数等仅对笛卡尔、平面、坐标有用。
上述高度评价的笔记似乎已经过时了几年。由于 mysql 5.7 有 ST_Distance_Sphere 可以做到这一点。
R
Richard Harrison

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

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

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

MySQL Spatial Extensions 是一个不错的选择,因为它们遵循 The OpenGIS Geometry Model。我没有使用它们,因为我需要保持我的数据库可移植。


谢谢,这很有帮助。从 2008 年开始阅读所有这些问题和答案,感觉很奇怪,因为这已经是 8 年前的事了。
@TheSexiestManinJamaica - 在 IEEE 754-1985 之前,计算机浮点硬件是混乱的。甚至在 a*b 不等于 b*a 的机器上(对于某些值)。有很多例子有点像:2+2 = 3.9999。该标准清理了很多混乱,并被几乎所有硬件和软件“迅速”采用。因此,这种讨论是有效的,不仅从 2008 年开始,而且持续了三分之一个世纪。
G
Gajus

取决于您需要的精度。

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 开始,考虑使用 Spatial Data Types (SDT),特别是 POINT 来存储单个坐标。在 5.7 之前,SDT 不支持索引(表类型为 MyISAM 时,5.6 除外)。

笔记:

使用 POINT 类时,存储坐标的参数顺序必须是 POINT(latitude, longitude)。

创建空间索引有一种特殊的语法。

使用 SDT 的最大好处是您可以访问空间分析函数,例如计算两点之间的距离 (ST_Distance) 并确定一个点是否包含在另一个区域内 (ST_Contains)。


您复制粘贴了先前答案的一部分,并用创建该表的人不推荐的内容“总结”:«如何分区?嗯,MySQL 很挑剔。所以 FLOAT/DOUBLE 出来了。十进制已出。所以,我们陷入了一些混乱。本质上,我们需要将 Lat/Lng 转换为某种大小的 INT 并使用 PARTITION BY RANGE。» AND «FLOAT 有 24 个有效位; DOUBLE 有 53 个。(它们不与 PARTITIONing 一起使用,但为了完整性而包含在内。通常人们使用 DOUBLE 时没有意识到它有多大的杀伤力,以及它占用了多少空间。)» 留下您编写的 SDT 部分。
@Armfoot如果您查看编辑时间,那是从我那里复制的另一个答案。这并不重要:我看到 Stack Overflow 更像是“为未来的我做的笔记”。
不,他没有从您那里复制,他只是像您从他在 2014 年引用的链接中所做的那样粘贴表格(您的帖子来自 2015 年)。顺便说一句,我认为您在链接 Spatial 数据类型时拼错了“Special”。您编写的这部分实际上对想要开始使用它们的人很有用,如果您添加更多示例,例如 CREATE TABLE geom (g GEOMETRY NOT NULL, SPATIAL INDEX(g)) ENGINE=MyISAM; 和有关 SDT 限制的警告,例如 James mentioned,也许您的答案会更简洁准确,以帮助其他人人也...
@Gajus - 我很荣幸你们两个找到了我的文件! (不,我不知道跳蚤有多大,但我觉得它会引起别人的注意。)
使用 POINT 类时,存储坐标的参数顺序必须是 POINT(longitude/X, latitude/Y)。
s
saeed khalafinejad

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


A
Alexander Holsgrove

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


DECIMAL 类型用于不接受 floor/ceil 的财务计算。普通的 FLOAT 明显优于 DECIMAL
@Kondybas - 由于数据库中的主要成本是获取行,因此浮点数和十进制数之间的性能差异不应该成为问题。
M
Mariano Peinador

无需走太远,根据谷歌地图,lat 和 lng 最好是 FLOAT(10,6)。


你从哪里得到这些信息我找不到它?以防万一发生变化。
@webfacer,这里的“在 MySQL 中创建表”部分:developers.google.com/maps/documentation/javascript/… 例如lat FLOAT( 10, 6 ) NOT NULL, lng FLOAT( 10, 6 ) NOT NULL
@webfacer,从 mysql 8.0.17 开始,FLOAT 语法似乎已被弃用。 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
而且,MySQL 总是忽略括号中的数字,这些数字可以选择性地装饰 FLOAT 和 DOUBLE 声明。
@webfacer 该链接不再包含该部分...
佚名

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

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


如果您有大量数据,则乘以某个大数(例如一百万)非常好,因为整数运算(例如索引检索)比浮点数快得多。
@KaitlinDuckSherwood - 位就是位 - 我不知道 32 位浮点数的检索(索引或其他方式)比 32 位整数慢的任何原因。如今,即使是浮动数学也足够快,不会成为问题。尽管如此,我同意将隐含乘数与整数一起使用的评论:它最大限度地提高了 32 位的精度。随着技术的进步,有点面向未来。
C
Community

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

如果您依靠 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 框架等中)。

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


不好。 OP 说他将对 lat/lng 对进行计算 - 你的答案排除了
T
The Godfather

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.2780818 和 X,21.2780819 之间有 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 浮点类型 reference。它们中的任何一个都将像普通的 FLOAT 并且无论如何都等于 4 个字节。

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


不仅仅是 NASA 需要高精度。土木工程师和建筑商也需要它,否则你会在停车场和建筑物中遇到大水坑,所有的大理石都滚到角落里。但测量员并不依赖标准的手机级 GPS。对于标准 GPS,FLOAT(IEEE488 32 位浮点格式)具有足够的精度。
K
Kaitlin Duck Sherwood

虽然它不是所有操作的最佳选择,但如果您正在制作地图图块或使用只有一个投影的大量标记(点)(例如墨卡托,如谷歌地图和许多其他滑动地图框架所期望的),我发现了什么我称“大坐标系”非常非常方便。基本上,您以某种方式放大存储 x 和 y 像素坐标——我使用缩放级别 23。这有几个好处:

您对墨卡托像素进行一次昂贵的 lat/lng 转换,而不是每次处理该点时

从给定缩放级别的记录中获取平铺坐标需要右移一次。

从记录中获取像素坐标需要一次右移和一次按位与。

移位非常轻量级,可以在 SQL 中执行,这意味着您可以执行 DISTINCT 以每个像素位置仅返回一条记录,这将减少后端返回的记录数,这意味着更少的处理前端。

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


T
Torben Brodt

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

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


你能在这里提供你的测试结果吗?
m
mlinuxgada

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


MySQL DOUBLE 中执行操作。 MySQL 允许您将数据存储为 4 字节 FLOAT 或 8 字节 DOUBLE。因此,将表达式存储到 FLOAT 列时可能会丢失精度。
m
mahfuz

纬度范围从 -90 到 +90(度),因此 DECIMAL(10, 8) 可以用于经度范围从 -180 到 +180(度),因此您需要 DECIMAL(11, 8)。

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

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


D
Dylan

PostGIS 中的空间函数比 MySQL 空间函数中的函数更实用(即不受 BBOX 操作的限制)。看看:link text


A
Anderson Ribeiro

我建议您对 SQL Server 使用 Float 数据类型。


u
ukgav

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

这是大约 10 厘米的精度,同时仅使用 5 个字节的存储空间。

例如 CAST(123.456789 作为十进制 (9,6))


B
BCS

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


float 和 decimal 类型都有自己的位置。根据经验,浮点数表示物理变量,小数表示可数实体(主要是金钱)。我不明白为什么你更喜欢小数作为纬度/经度
我也认为浮点数适用于纬度/经度。至少在 SQL Server 上(4 字节,7 位)。
浮子估计不准,准时的湖水是致命的!它可以将您指向地球上一个完全不同的地方。
浮点数据类型的最大错误足够低,这应该不是问题。我的意思是,无论如何,您都必须注意两种实现的错误乘法/累积。
@HLGEM - 四舍五入到一些小数位也会让你在地球上的不同位置。问题是那个不同的地点是否如此接近以至于无关紧要。
C
ConroyP

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

如果您的 MySQL 版本早于 5.0.3,您可能需要注意某些 floating point comparison errors

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


您需要一个真正的纬度/经度坐标数据类型以便于计算。想象一下类似“select * from stores where distance(stores.location, mylocation) < 5 英里”的便利性
之前没有听说过空间扩展,这听起来确实很方便,之前曾在一个继承的应用程序上工作过,该应用程序做了很多与地理相关的计算,必须检查一下。
@ConroyP - 不。该引用指出 DECIMAL 由于使用浮动实现而(在 5.0.3 之前)存在某些错误。