当存储纬度或经度数据在ANSI SQL兼容的数据库,什么数据类型将是最合适的?应该用浮点数,还是十进制,还是…?
我知道Oracle、MySql和SQL Server已经添加了一些专门用于处理地理数据的特殊数据类型,但我感兴趣的是如何将信息存储在“普通”SQL数据库中。
当存储纬度或经度数据在ANSI SQL兼容的数据库,什么数据类型将是最合适的?应该用浮点数,还是十进制,还是…?
我知道Oracle、MySql和SQL Server已经添加了一些专门用于处理地理数据的特殊数据类型,但我感兴趣的是如何将信息存储在“普通”SQL数据库中。
当前回答
你可以很容易地将一个拉/长十进制数存储在一个无符号整数字段中,而不是将它们分成整数和小数部分,并使用下面的转换算法分别存储它们:
作为一个存储的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
这需要存储在一个无符号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 */
其他回答
我会用一个精确的小数来表示你的数据。
在普通的Oracle中,被称为LOCATOR (Spatial的一个残缺版本)的特性要求使用NUMBER(没有精度)数据类型存储坐标数据。当你试图创建基于函数的索引来支持空间查询时,它会卡住。
你可以很容易地将一个拉/长十进制数存储在一个无符号整数字段中,而不是将它们分成整数和小数部分,并使用下面的转换算法分别存储它们:
作为一个存储的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
这需要存储在一个无符号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 */
好吧,你问如何存储纬度/经度,我的回答是:不要,你可以考虑使用WGS 84(在欧洲ETRS 89),因为它是地理参考的标准。
但是在SQL 2008最终包含地理位置支持之前,我使用了一个用户定义类型。
我们使用浮点数,但任何带有6位小数的数字也可以。