8 位小数的纬度/经度应该使用哪种 MySQL 数据类型?

Posted

技术标签:

【中文标题】8 位小数的纬度/经度应该使用哪种 MySQL 数据类型?【英文标题】:What MySQL data type should be used for Latitude/Longitude with 8 decimal places? 【发布时间】:2012-09-12 07:44:29 【问题描述】:

我正在处理地图数据,Latitude/Longitude 扩展到小数点后 8 位。例如:

Latitude 40.71727401
Longitude -74.00898606

我在Google document看到了 使用:

lat FLOAT( 10, 6 ) NOT NULL,  
lng FLOAT( 10, 6 ) NOT NULL

但是,它们的小数位只能到 6。 我应该使用FLOAT(10, 8) 还是有另一种方法可以考虑存储这些数据,所以它是精确的。它将与地图计算一起使用。谢谢!

【问题讨论】:

你真的需要在地球表面存储值accurate to 1.1mm吗?如果是这样,那你为什么首先将值存储在 latlng 中? What is the ideal data type to use when storing latitude / longitudes in a mysql database?的可能重复 谷歌文档是错误的!不要使用 float 类型 - 只有 7 位精度。您至少需要 9。您不需要 10 - 文档出于某种奇怪的原因将减号计为数字。执行以下任一操作:double(9,6)decimal(9,6) 真正需要多少精度? 6 位小数为您提供足够的精度来区分两个互相接吻的人。 8 可以区分你的手指。 FLOAT 区分相距 1.7m (5.6ft) 的两个项目。所有这些对于“地图”应用程序来说都太过分了! 还有,跨平台复制dba.stackexchange.com/q/107089 【参考方案1】:

MySQL 支持Spatial data types 和Point 是可以使用的单值类型。示例:

CREATE TABLE `buildings` (
  `coordinate` POINT NOT NULL,
  /* Even from v5.7.5 you can define an index for it */
  SPATIAL INDEX `SPATIAL` (`coordinate`)
) ENGINE=InnoDB;

/* then for insertion you can */
INSERT INTO `buildings` 
(`coordinate`) 
VALUES
(POINT(40.71727401 -74.00898606));

【讨论】:

也许我的回答误用了精确这个词,因为 DECIMAL 仍然只与您给出的精度一样准确。我的意思是它那么准确。当然,有些计算会扩大误差。如果我有一个十进制 x,那么 sin(x^100) 将会很遥远。但是,如果(使用 DECIMAL (10, 8) 或 FLOAT (10, 8))我计算 0.3 / 3,那么 DECIMAL 给出 0.100000000000(正确),而 float 给出 0.100000003974(正确到 8dp,但如果相乘会是错误的)。我知道主要区别在于数字的存储方式。 DECIMAL 存储十进制数字,FLOAT 存储二进制近似值。 由于对精确度的怀疑,我要加倍。 8 位小数是 1.1 毫米(小于 1/16 英寸)精度。为什么你需要它来表示纬度和经度? Facebook 似乎对 lat 使用最多 12 位小数,对 lng 使用 13 位小数。 vartec 写道,8 位小数等于 1.1 毫米; 7和6呢? (我数学不好)。我现在使用 double 但想检查是否可以通过更改类型来获得距离计算。谢谢。 这个问题的答案 (gis.stackexchange.com/questions/8650/…) 提供了有关使用不同经纬度小数位数获得的精度的信息。【参考方案2】:

在 laravel 中使用 十进制列 类型进行迁移

$table->decimal('latitude', 10, 8);
$table->decimal('longitude', 11, 8);

更多信息see可用的列类型

【讨论】:

laravel 也有要点。为什么要使用浮点数或为什么要使用点数? 我也想知道这里有什么优势 这个也支持负数吗?【参考方案3】:

此外,您会看到float 值已四舍五入。

// 例如:给定值 41.0473112,29.0077011 浮动(11,7) |十进制(11,7) -------------------------- 41.0473099 | 41.0473112 29.0077019 | 29.0077011

【讨论】:

您可以使用double 数据类型,它具有所需的精度。 给我看一张可以区分这两点的有用地图。我声称这两种表述都是“不必要的精确”。【参考方案4】:

不要使用浮点数...它会使你的坐标四舍五入,导致一些奇怪的事件发生。

使用小数

【讨论】:

【参考方案5】:

我相信在 MySQL 中存储 Lat/Lng 的最佳方式是使用带有 SPATIAL 索引的 POINT 列(2D 数据类型)。

CREATE TABLE `cities` (
  `zip` varchar(8) NOT NULL,
  `country` varchar (2) GENERATED ALWAYS AS (SUBSTRING(`zip`, 1, 2)) STORED,
  `city` varchar(30) NOT NULL,
  `centre` point NOT NULL,
  PRIMARY KEY (`zip`),
  KEY `country` (`country`),
  KEY `city` (`city`),
  SPATIAL KEY `centre` (`centre`)
) ENGINE=InnoDB;


INSERT INTO `cities` (`zip`, `city`, `centre`) VALUES
('CZ-10000', 'Prague', POINT(50.0755381, 14.4378005));

【讨论】:

你能解释一下为什么吗?你如何运行选择? @Toskan,因为 MySQL 的 POINT 是几何数据类型,表示坐标空间中的单个位置,可用于空间索引。 @Toskan,您可以简单地SELECT centre ... 获取坐标,也可以分别SELECT ST_X(centre), ST_Y(centre) ... 获取纬度和经度。见point property functions 为什么它比我的意思是浮动更好。你可以并不意味着你应该。我知道它存在,我知道你可以做到。但是你也可以从高楼上跳下来。并不意味着你应该。做select * from mytable where ST_X(Center) = 7.51234; 听起来可能有问题。 @Toskan,这样做的全部目的是在单个列和单个 R 树索引中拥有 2d 数据,即坐标。然后您可以轻松地按球体上的距离排序,过滤多边形内的条目等。【参考方案6】:

自从提出这个问题以来,MySQL 现在支持空间数据类型。因此,当前接受的答案没有错,但如果您正在寻找附加功能,例如查找给定多边形内的所有点,请使用 POINT 数据类型。

Checkout the Mysql Docs on Geospatial data types 和 spatial analysis functions

【讨论】:

【参考方案7】:

您可以将数据类型设置为有符号整数。当您将坐标存储到 SQL 时,您可以设置为 lat*10000000 和 long*10000000。当您选择距离/半径时,您会将存储坐标划分为 10000000。我用 300K 行对其进行了测试,查询响应时间很好。 (2 x 2.67GHz CPU, 2 GB RAM, MySQL 5.5.49)

【讨论】:

哪个更快?这样做还是使用浮点数或小数? @Dinidiniz - 速度差异非常小。获取行会压倒任何数据库操作的时间。 为什么是 10000000?如果它包含十进制值后超过 6 位,会发生什么情况?或者它总是返回 6 个小数点。 @MahbubMorshed - 你的意思是 7 位 - 显示了 7 个零位。但是,是的,这种技术总是准确地存储 7 位数字,仅此而已。 (如果使用 4 字节整数,则不能将乘数增加到 7 位以上,因为经度值可以大到 180,并且必须避免溢出有符号整数最大值。)这比存储在单精度浮点中精确 2 位,在大经度值处只有大约 5 位到小数点右侧。 (179.99998 和 179.99997 可以存储为相同的浮点值;179.99996 远离 179.99998)。)【参考方案8】:

6 位小数的精度约为 16 厘米,这意味着如果两个对象之间的距离小于 16 厘米,则它们具有相同的 lat 和 lng。

另外,在 mariadb/mysql 中,如果我们有大量数据用于索引,使用 float/double 并不理想,而 Point 数据类型对于数据大小来说是开销。最好使用十进制或将 lat long 转换为 INT。

使用带 6 位小数的小数是一个不错的选择,因为我们可以忽略转换,我们只有 16 厘米的误差,经度在 -180 到 180 之间,因此需要比纬度多 1 位,在 -90 到 90 度之间:

Lat DECIMAL(8,6)
Lng DECIMAL(9,6) 

我们可以扩展到小数点后 8 位:

Lat DECIMAL(10,8)
Lng DECIMAL(11,8) 

MySQL reference

Mariadb reference

【讨论】:

与***.com/questions/159255/… 交叉引用以获取有关此精度的更多信息【参考方案9】:
CREATE TABLE your_table_name (
   lattitude  REAL,
   longitude  REAL
)

还可以考虑在您的经纬度声明中添加进一步的验证:

CREATE TABLE your_table_name (
   lattitude  REAL CHECK(lattitude IS NULL OR (lattitude >= -90 AND lattitude <= 90)),
   longitude  REAL CHECK(longitude IS NULL OR (longitude >= -180 AND longitude <= 180))
)

解释: https://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html

【讨论】:

【参考方案10】:

在 Rails 上使用迁移 ruby​​

class CreateNeighborhoods < ActiveRecord::Migration[5.0]
  def change
    create_table :neighborhoods do |t|
      t.string :name
      t.decimal :latitude, precision: 15, scale: 13
      t.decimal :longitude, precision: 15, scale: 13
      t.references :country, foreign_key: true
      t.references :state, foreign_key: true
      t.references :city, foreign_key: true

      t.timestamps
    end
  end
end

【讨论】:

这不会将经度限制为 -99..99 吗?这不包括太平洋的大部分地区! 这是一个例子,不应被视为绝对真理。您可以使用另一个 DECIMAL 十进制精度(20、18)等等...如果您需要保存地理和空间数据,您可以使用 postgis 数据库来实现此目的。 MySQL 空间扩展是一个很好的选择,因为它们遵循 OpenGIS 几何模型。我没有使用它们,因为我需要保持我的数据库可移植。 postgis.net (20,18) 也达到 +/-99。 这是一个例子,不应被视为绝对真理。您可以使用另一个 DECIMAL 十进制精度(20、18)等等...如果您需要保存地理和空间数据,您可以使用 postgis 数据库来实现此目的。 MySQL 空间扩展是一个很好的选择,因为它们遵循 OpenGIS 几何模型。我没有使用它们,因为我需要保持我的数据库可移植。 postgis.net 老兄这只是一个例子,你可以使用你想要的精度,如果十进制不能帮助你使用 postgis 一个专为地理和空间数据制作的数据库【参考方案11】:

你应该简单地使用varchar (20)

【讨论】:

我真的怀疑 VARCHAR 是否适合这个,特别是 VARCHAR(20)

以上是关于8 位小数的纬度/经度应该使用哪种 MySQL 数据类型?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL查询在一定范围内拉经度

经纬度校验

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

位置坐标,纬度和经度的长度是多少? [关闭]

纬度和经度的数据类型是啥?

经纬度小数点后5位是多少米