Php 地理位置MySQL查询

Php 地理位置MySQL查询,php,mysql,geolocation,Php,Mysql,Geolocation,我经营一个基于地理位置的社交网络。成员可以根据彼此的距离查看其他成员 现在,我的MySQL查询如下所示: $lat_min = $geo['user_lat'] - 5; $lat_max = $geo['user_lat'] + 5; $long_min = $geo['user_long'] - 5; $long_max = $geo['user_long'] + 5; $members_query = "SELECT " . $fields . " FROM members WH

我经营一个基于地理位置的社交网络。成员可以根据彼此的距离查看其他成员

现在,我的MySQL查询如下所示:

$lat_min = $geo['user_lat'] - 5;
$lat_max = $geo['user_lat'] + 5;
$long_min = $geo['user_long'] - 5;
$long_max = $geo['user_long'] + 5;    

$members_query = "SELECT " . $fields . " FROM members WHERE (user_lat BETWEEN " . $lat_min . " AND " . $lat_max . " AND user_long BETWEEN " . $long_min . " AND " . $long_max . ") OR (gc_lat BETWEEN " . $lat_min . " AND " . $lat_max . " AND gc_long BETWEEN " . $long_min . " AND " . $long_max . ")";
user_lat
user_long
是基于地理位置的坐标,前提是它们在浏览器中打开了地理位置
gc_lat
gc_long
以及基于其IP地址的坐标。这些行都在数据库中建立了索引。我把所有成员拉到10度以内

问题是,对于我们250000多名会员来说,这个查询大约需要2秒钟的时间,我们希望网站能够扩展

尝试2:我已尝试为每个成员分配象限,例如“36x-99”。。。我将纬度和经度四舍五入到3的最接近倍数来标记象限,然后我只在成员所在象限的12度范围内绘制象限

$members_query = "SELECT " . $fields . " FROM members WHERE quadrant IN ('36x-99', '33x-99', '30x-99', ...);
这使我在查询速度上没有明显的差异


有人知道我该怎么做吗?我需要找到一个解决方案,使站点能够更好地扩展。

问题在于,您在数据库中存储数据的方式不适合您正在执行的任务类型。在
几何体
数据点中使用
值是一种方法。实际上,4年前就已经为此编写了一些代码,但在查找时遇到了问题。但似乎覆盖得很好

编辑好的,找到了我的旧代码,但它指的是我显然无法共享的旧客户端数据。但是在数据库中使用坐标的关键是使用数据库表中存储的
几何体类型的
数据。在官方MySQL网站上。因为我需要一个理由来重温这种类型的代码和概念一段时间,所以这里有一个快速的MySQL脚本,我用示例数据创建了一个示例表来传达基本概念。一旦你了解了正在发生的事情,就会有很多很酷的选择

也发现了这个概念

并在MySQL 5.6中找到。关于索引和性能的大量信息。特别是关于MySQL空间索引性能:

MyISAM表支持空间索引,因此上述查询将使用这些索引

另一方面:

InnoDB引擎不支持空间索引,因此这些查询会很慢

下面是我的基本MySQL测试脚本,以帮助说明这一概念:

/* Create the database `spatial_test` */
CREATE DATABASE `spatial_test` CHARACTER SET utf8 COLLATE utf8_general_ci;

/* Create the table `locations` in `spatial_test` */
CREATE TABLE `spatial_test`.`locations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `coordinates` point NOT NULL,
  UNIQUE KEY `id` (`id`),
  SPATIAL KEY `idx_coordinates` (`coordinates`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

/* Insert some test data into it. */
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(27.174961 78.041822)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(27.985818 86.923596)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(44.427963 -110.588455)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(19.896766 -155.582782)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(40.748328 -73.985560)'));
INSERT INTO `spatial_test`.`locations` (`id`, `coordinates`) VALUES (NULL, GeomFromText('POINT(40.782710 -73.965310)'));

/* A sample SELECT query that extracts the 'latitude' & 'longitude' */
SELECT x(`spatial_test`.`locations`.`coordinates`) AS latitude, y(`spatial_test`.`locations`.`coordinates`) AS longitude FROM `spatial_test`.`locations`;

/* Another sample SELECT query calculates distance of all items in database based on GLength using another set of coordinates. */
SELECT GLength(LineStringFromWKB(LineString(GeomFromText(astext(PointFromWKB(`spatial_test`.`locations`.`coordinates`))), GeomFromText(astext(PointFromWKB(POINT(40.782710,-73.965310))))))) AS distance
FROM `spatial_test`.`locations`
;

/* Yet another sample SELECT query that selects items by using the Earth’s radius. The 'HAVING distance < 100' equates to a distance of less than 100 miles or kilometers based on what you set the query for. */
/* Earth’s diameter in kilometers: 6371 */
/* Earth’s diameter in miles: 3959 */
SELECT id, (3959 * acos(cos(radians(40.782710)) * cos(radians(x(`spatial_test`.`locations`.`coordinates`))) * cos(radians(y(`spatial_test`.`locations`.`coordinates`)) - radians(-73.965310)) + sin(radians(40.782710)) * sin(radians(x(`spatial_test`.`locations`.`coordinates`))))) AS distance 
FROM `spatial_test`.`locations`
HAVING distance < 100
ORDER BY id
;
/*创建“空间测试”数据库*/
创建数据库“空间测试”字符集utf8校对utf8\U常规\U ci;
/*在“空间测试”中创建表“位置”*/
创建表“空间测试”。“位置”(
`id`int(11)非空自动增量,
`坐标`点不为空,
唯一键'id'('id'),
空间键'idx_坐标'('coordinates`)
)ENGINE=MyISAM默认字符集=utf8自动增量=1;
/*在其中插入一些测试数据*/
在“空间测试”中插入“位置”('id','coordinates')值(NULL,GeomFromText('POINT(27.174961 78.041822));
在“空间_测试”.“位置”(“id”、“坐标”)中插入值(NULL,GeomFromText('POINT(27.985818 86.923596));
在“空间测试”中插入“位置”('id','coordinates')值(NULL,GeomFromText('POINT(44.427963-110.588455));
插入'spatical_test'.'locations'('id','coordinates')值(NULL,GeomFromText('POINT(19.896766-155.582782)');
在'spatial_test'.'locations'('id','coordinates')值(NULL,GeomFromText('POINT(40.748328-73.985560)')中插入;
在'spatial_test'.'locations'('id','Coordinations')值(NULL,GeomFromText('POINT(40.782710-73.965310)')中插入;
/*提取“纬度”和“经度”的示例SELECT查询*/
选择x(`spatial_test`.`locations`.`坐标`)作为纬度,选择y(`spatial_test`.`locations`.`坐标`)作为从`spatial_test`.`locations`中选择的经度;
/*另一个示例SELECT查询使用另一组坐标基于GLength计算数据库中所有项的距离*/
选择GLength(LineStringFromWKB(LineString(GeomFromText)(astext(PointFromWKB(`spatical_test`.`locations`.`coordinates`))))、GeomFromText(PointFromWKB(POINT(POINT)(40.782710,-73.965310』))作为距离
从“空间测试”到“位置”`
;
/*另一个示例SELECT查询使用地球半径选择项目。“距离小于100”等于根据您设置的查询距离小于100英里或公里*/
/*地球直径(公里):6371*/
/*地球直径(英里):3959*/
选择id,(3959*acos(cos(弧度(40.782710))*cos(弧度(x('spatical_test`.'locations`.'coordinates`))*cos(弧度(y('spatical_test`.'locations`.'coordinates`))-radians(-73.965310))+sin(弧度(40.782710))*sin(弧度(x('spatical_test`.'locations`.'coordinates`.))作为距离
从“空间测试”到“位置”`
距离小于100的
按id订购
;

OK的可能重复,我会调查一下。看来你的思路是对的。我明天就要弄清楚了。“我唯一想知道的是它会计算出绕地球的距离,还是穿过地球?”这是一个很好的问题。这个问题似乎解释得很好。我在数据库中得到了我的坐标:太好了!我刚刚编辑了我的答案,添加了一个基于地球直径的查询。谢谢你再次帮助我关心这些东西!该查询基于我喜欢在上面发表的一篇帖子以及此处的Google Maps API文档: