您需要做一些事情才能将 MySQL 地理空间扩展用于此类事情。
- 您必须为您的地理空间数据使用 MyISAM 表。
- 您的表中需要有一个
GEOMETRY 列,并且它不能为空。
- 您不能将问题中提到的球余弦定律距离计算用作主要搜索标准。您需要改用边界框(也就是说,您需要在搜索点的特定距离内搜索点。
您当前的查询只是通过检查您的地点表中的所有点并按邻近顺序对它们进行排序来找到最接近 (37.77671, -122.3980) 的点。当您使用地理空间时,您正在做一些比这更聪明的事情。您需要建立一个接近限制。我们称其为 10.0 法定英里的半径。请注意,每个纬度有 69.0 法定英里。
那么你是怎么做到这一切的呢?
首先为自己制作一个带有几何列的表格,可能是这样的:
CREATE TABLE geoplace (
id INT NOT NULL,
geo GEOMETRY NOT NULL,
PRIMARY KEY id (id),
SPATIAL KEY geo (geo)
) ENGINE=MYISAM
然后,像这样填充它,将几何 Point 项放入每个 geo 值中。
INSERT INTO geoplace
SELECT DISTINCT id,
GEOMFROMTEXT(
CONCAT('POINT(',
lat,
' ',
lng,
')') ) AS geo
FROM places
显然,您可以将 GEOMETRY 列放入您想要的任何表格中。但是您不能在 InnoDB 表中创建有效的空间索引;该访问方法不支持空间索引。
然后,您需要这样的查询来检索您的信息。
SELECT id, X(geo), Y(geo), distance FROM (
SELECT id, geo,r,
units * DEGREES( ACOS(
COS(RADIANS(latpoint))
* COS(RADIANS(X(geo)))
* COS(RADIANS(longpoint) - RADIANS(Y(geo)))
+ SIN(RADIANS(latpoint))
* SIN(RADIANS(X(geo))))) AS distance
FROM geoplace
JOIN (
SELECT 37.776708 AS latpoint, -122.398050 AS longpoint,
10.0 AS r, 69.0 AS units
) AS p ON (1=1)
WHERE MbrContains(GeomFromText(
CONCAT('LINESTRING(',
latpoint-(r/units),' ',
longpoint-(r /(units* COS(RADIANS(latpoint)))),
',',
latpoint+(r/units) ,' ',
longpoint+(r /(units * COS(RADIANS(latpoint)))),
')')), geo)
) AS d
WHERE distance <= r
ORDER BY distance
请注意,这个巨大的查询使用了您在本子句中提供的参数:
SELECT 37.776708 AS latpoint, -122.398050 AS longpoint,
10.0 AS r, 69.0 AS units
它将使用MbrContains 进行空间矩形搜索,然后使用球余弦定律公式计算找到的点的距离,然后正确排序。与您开始使用的查询相比,这件事运行得非常快,因为它利用了空间索引。
这里有更详细的描述。
http://www.plumislandmedia.net/mysql/using-mysqls-geospatial-extension-location-finder/
这里有背景。
http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/