【问题标题】:What datatype to use when storing latitude and longitude data in SQL databases? [duplicate]在 SQL 数据库中存储纬度和经度数据时使用什么数据类型? [复制]
【发布时间】:2010-11-14 20:22:04
【问题描述】:

在符合 ANSI SQL 的数据库中存储纬度或经度数据时,哪种数据类型最合适?应该使用float,还是decimal,还是...?

我知道 Oracle、MySql 和 SQL Server 添加了一些专门用于处理地理数据的特殊数据类型,但我对如何将信息存储在“普通”SQL 数据库中感兴趣。

【问题讨论】:

  • Atul 是对的,这不是同一个问题,因为另一个问题指定将在 lat/lng 上进行计算。我刚刚意识到我的另一个答案最适合这里,所以请看一下:stackoverflow.com/a/25120203/1226018

标签: sql database geocoding latitude-longitude


【解决方案1】:

纬度使用:Decimal(8,6),经度使用:Decimal(9,6)

如果你不习惯精度和缩放参数,这里有一个格式字符串视觉效果:

纬度和经度 ##.#########.######

精确到小数点后 6 位应该可以让您在坐标上达到大约 10 厘米的精度。

【讨论】:

  • +1:通过使用固定的小数位数来避免四舍五入错误。
  • 出于他们的目的,维基百科建议:“避免过度精确(0.0001° en.wikipedia.org/wiki/… 为了使我的字段更加精确,我还决定使用小数点右侧的六位数字和左侧的三位数字。我很高兴这个答案证实了这是一个不错的决定。
  • 另外,如果您不打算对数字进行操作,请考虑将它们存储为字符串。使用varchar(20) 会更加精确,而且存储空间也很便宜。
  • (8,6) 表示纬度,(9,6) 表示经度
  • @Makito 纬度从 -90 度到 90 度......所以只需要小数点左边 2 位......
【解决方案2】:

我们使用浮点数,但任何带有 6 位小数的数字也可以。

【讨论】:

  • 计算可能会引入额外的数字,这只是噪音。十进制到二进制的转换也会使最后的小数位可疑并且可能是错误的。出于某些目的很好。导航很糟糕。
  • 我不认为建议浮动应该降价 - 你让我猜测并在其他地方搜索答案:stackoverflow.com/questions/551894/…
  • 问题是关于存储的。 32 位浮点数是最有效的存储方式。如果您需要进行大量计算,并且可以证明舍入误差很重要,请先转换为 64 位浮点数。
【解决方案3】:

我会为您的数据使用具有适当精度的小数。

【讨论】:

    【解决方案4】:

    好吧,您问如何存储纬度/经度,我的回答是:不要,您可以考虑使用 WGS 84(在欧洲为 ETRS 89),因为它是地理参考的标准。

    但除此之外,在 SQL 2008 最终包含地理支持之前,我使用了用户定义类型。

    【讨论】:

    • 我很好奇,你还记得你建议不要保存经纬度是什么意思吗?你以为你会保存什么?
    • 我不确定 Kasper 在这里说什么。 Lat/Long 是一种位置格式(相当于 UTM、MGRS、NZTM 等),而 WGS84 是一个基准 - 一个应用位置坐标的参考系统。您需要都知道您的坐标和您正在使用的基准面,尽管对于在线使用而言,它几乎总是 WGS84。
    【解决方案5】:

    在原版 Oracle 中,名为 LOCATOR(Spatial 的残缺版本)的功能要求使用 NUMBER 数据类型(无精度)存储坐标数据。当您尝试创建基于函数的索引以支持空间查询时,它会作呕。

    【讨论】:

      【解决方案6】:

      您可以轻松地将纬度/经度十进制数存储在无符号整数字段中,而不是将它们拆分为整数和小数部分,然后使用以下转换算法将它们分开存储:

      作为存储的mysql函数:

      CREATE DEFINER=`r`@`l` FUNCTION `PositionSmallToFloat`(s INT) 
      RETURNS decimal(10,7)
      DETERMINISTIC
      RETURN if( ((s > 0) && (s >> 31)) , (-(0x7FFFFFFF - 
      (s & 0x7FFFFFFF))) / 600000, s / 600000)
      

      返回

      CREATE DEFINER=`r`@`l` FUNCTION `PositionFloatToSmall`(s DECIMAL(10,7)) 
      RETURNS int(10)
      DETERMINISTIC
      RETURN s * 600000
      

      这需要存储在 unsigned int(10) 中,这适用于 mysql 以及无类型的 sqlite。

      根据经验,我发现它的运行速度非常快,如果您只需要存储坐标并检索这些坐标以进行数学运算。

      在 php 中这两个函数看起来像

      function LatitudeSmallToFloat($LatitudeSmall){
         if(($LatitudeSmall>0)&&($LatitudeSmall>>31)) 
           $LatitudeSmall=-(0x7FFFFFFF-($LatitudeSmall&0x7FFFFFFF))-1;
         return (float)$LatitudeSmall/(float)600000;
      }
      

      又回来了:

      function LatitudeFloatToSmall($LatitudeFloat){
         $Latitude=round((float)$LatitudeFloat*(float)600000);
         if($Latitude<0) $Latitude+=0xFFFFFFFF;
         return $Latitude;
      }
      

      这在创建例如带有整数的 memcached 唯一键方面也有一些额外的优势。 (例如:缓存地理编码结果)。希望这能为讨论增加价值。

      另一个应用程序可能是当您没有 GIS 扩展并且只想保留几百万个纬度/经度对时,您可以在 mysql 中的这些字段上使用分区以受益于它们是整数的事实:

      Create Table: CREATE TABLE `Locations` (
        `lat` int(10) unsigned NOT NULL,
        `lon` int(10) unsigned NOT NULL,
        `location` text,
        PRIMARY KEY (`lat`,`lon`) USING BTREE,
        KEY `index_location` (`locationText`(30))
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8
      /*!50100 PARTITION BY KEY ()
      PARTITIONS 100 */
      

      【讨论】:

        【解决方案7】:

        您应该看看 SQL Server 2008 中引入的新空间数据类型。它们是专门为此类任务设计的,使索引和查询数据更加容易和高效。

        http://msdn.microsoft.com/en-us/library/bb933876(v=sql.105).aspx

        【讨论】:

          【解决方案8】:

          我认为这取决于您最常需要执行的操作。

          如果您需要将完整值作为十进制数,请使用具有适当精度和小数位数的十进制。我相信,浮动远远超出了您的需求。

          如果您经常转换到/从 degºmin'sec" 分数表示法,我会考虑将每个值存储为整数类型(smallint、tinyint、tinyint、smallint?)。

          【讨论】:

            猜你喜欢
            • 2015-05-24
            • 2012-12-01
            • 2010-09-14
            • 2010-11-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多