Php 距离查询

Php 距离查询,php,mysql,sql,geospatial,spatial,Php,Mysql,Sql,Geospatial,Spatial,不知道如何建立这样一个开放的专家: 我有一个db中的客户列表,这些客户正在寻找他们所在位置半径范围内的活动。我可以存储他们的zipcode(或lat/lng)和他们参加活动的最大距离。所以列lat,lng,距离(例如:lat='22.7447858',lng='-82.1398589',距离=25) 全天发布事件,并存储其zipcode/lat/lng 我想运行一个查询(每天一次)来获取活动的客户。我在看《网络瘾君子》的帖子,但问题是我的查询方向相反。我需要找到“圈距离”涵盖当前活动的客户,而不

不知道如何建立这样一个开放的专家:

我有一个db中的客户列表,这些客户正在寻找他们所在位置半径范围内的活动。我可以存储他们的zipcode(或lat/lng)和他们参加活动的最大距离。所以列lat,lng,距离(例如:lat='22.7447858',lng='-82.1398589',距离=25)

全天发布事件,并存储其zipcode/lat/lng

我想运行一个查询(每天一次)来获取活动的客户。我在看《网络瘾君子》的帖子,但问题是我的查询方向相反。我需要找到“圈距离”涵盖当前活动的客户,而不是相反。不确定如何存储圆距离(上面的3列是否足够好,或者是否有更好的方法存储此类查询的数据)?不确定如何查询每个事件的客户

提前谢谢

您想要的是:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance
SELECT*FROM customer,event WHERE()
这将简单地获取所有客户和所有事件,将它们组合在一起以获得所有可能的组合(100个客户和10个事件提供1000个组合),然后检查它们是否在范围内*

我个人建议做一个
距离(客户、事件)
函数来为您计算距离。这样更容易管理查询,并且您可以重复使用它


*不一定按此顺序

从点A到点B的距离将与从点B到点A的距离相同(除非您处理的是道路方向和不同的路径)

基本上你要做的就是(在sql伪代码中)


SELECT distance(event_loc,user_loc)我认为有两种主要的方法可以做到这一点:动态计算距离,预先计算距离一次,然后将它们存储在查找表中

选项1,动态计算。汤姆·范德沃尔德的回答很好地解释了你将如何做到这一点。伪代码查询如下所示:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance
select id1 from Distances 
    join EventTable on id2=EventTable.eventid 
    join UserTable on id1=UserTable.userid 
where type2=<EVENT_TYPE> and type1=<USER_TYPE> 
    and geodistance_km_by_obj(id1,<USER_TYPE>,id2,<EVENT_TYPE>) < UserTable.max_distance
那么哪一个更好?这是CPU时间和磁盘空间之间的折衷,因此答案取决于您的资源。选项1(动态计算)需要DBMS做更多的工作(更多的CPU时间)。随着人员和事件数量的增加,该查询将需要更长的时间运行。选项2(预计算距离)将使查找速度非常快,但折衷是您必须将所有这些预计算的距离存储在磁盘上。您还需要努力确保查找表是最新的。每次添加、删除客户或事件或更改其lat/long时,您都需要相应地更新查找表。可以帮助您使此过程自动化;只要确保您尝试测试每个场景(添加、删除、移动),以确保查找表按照预期的方式得到更新


简短回答:如果数据库负载很小和/或磁盘空间有限,请选择选项1(动态计算)。如果负载很重,但磁盘空间充足,请选择选项2。选项2是更可能出现的情况,并且更具可扩展性。

如果您的距离计算与中的类似,则可以执行以下操作:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance
select id1 from Distances 
    join EventTable on id2=EventTable.eventid 
    join UserTable on id1=UserTable.userid 
where type2=<EVENT_TYPE> and type1=<USER_TYPE> 
    and geodistance_km_by_obj(id1,<USER_TYPE>,id2,<EVENT_TYPE>) < UserTable.max_distance
从距离中选择id1
在id2=EventTable.eventid上加入EventTable
在id1=UserTable.userid上连接UserTable
其中type2=和type1=
和地球距离(id1,id2,)
除了其他答案之外,您可能会考虑使用笛卡尔坐标(x、y和z)代替lat/lng进行db存储,因为结果查询表达式在db服务器上的负载/时间方面比可能的lat/lng距离查询更简单

PHP实现的示例可以在下面找到:

“getCartesian”方法将lat/lng转换为笛卡尔坐标,“getDistanceByCartesian”方法显示如何计算实际距离。您需要做的是将这个距离计算从PHP传输到SQL查询(应该没有那么复杂)

编辑,因为我发现是时候给出一个更实际的例子了

根据您可以在上述链接下找到的类别,我为我的公司地点和附近的所有MC Donalds餐厅设置了2个演示表,并将lat/lng从谷歌地图转换为笛卡尔x、y、z:

CREATE TABLE `locations` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL DEFAULT '',
  `lat` double NOT NULL,
  `lng` double NOT NULL,
  `x` double NOT NULL,
  `y` double NOT NULL,
  `z` double NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `locations` (`id`, `title`, `lat`, `lng`, `x`, `y`, `z`)
VALUES
    (1,'Ida-Ehre-Platz 10, 20095 Hamburg',53.55053,9.99949,3727600.05477,657242.251356,5124712.81705),
    (2,'Kieler Straße 191-193, 22525 Hamburg',53.57731,9.93686,3725956.4981,652753.812254,5126481.40905),
    (3,'Reeperbahn 42, 20359 Hamburg',53.549951,9.964937,3728046.74189,655003.113578,5124674.56664),
    (4,'Theodor-Heuss-Platz 3, 20354 Hamburg',53.56083,9.99038,3726797.15378,656489.722425,5125393.17725),
    (5,'Mundsburger Damm 67, 22087 Hamburg',53.57028,10.02642,3725550.98379,658686.623655,5126017.24553),
    (6,'Paul-Nevermann-Platz 1, 22765 Hamburg',53.552602,9.936678,3728135.78521,653123.397726,5124849.69505),
    (7,'Friedrich-Ebert-Damm 101, 22047 Hamburg',53.58753,10.08958,3723303.02881,662522.688778,5127156.05819),
    (8,'Amsinckstraße 73, 20097 Hamburg',53.54271,10.02654,3727978.07563,659123.791421,5124196.16112),
    (9,'Eiffestraße 440, 20537 Hamburg',53.55214,10.04638,3726919.13256,660267.521487,5124819.17553);


CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL DEFAULT '',
  `lat` double NOT NULL,
  `lng` double NOT NULL,
  `x` double NOT NULL,
  `y` double NOT NULL,
  `z` double NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `user` (`id`, `name`, `lat`, `lng`, `x`, `y`, `z`)
VALUES
    (1,'Ministry.BBS, Cremon 36, 20457 Hamburg',53.545943,9.988761,3728127.10678,656615.385203,5124409.77226),
    (2,'BBS, Dorotheenstraße 60, 22301 Hamburg',53.583231,10.008315,3724617.80169,657307.963226,5126872.28974);
基于这两个表,查找每个用户(我们公司的办公室)在一定距离(本例中为2000米)内的所有位置(餐厅)的SQL查询将是:

SELECT locations.*,
    2 * 6371000.785 *
        asin(
            sqrt(
                pow(locations.x - user.x, 2)
                + pow(locations.y - user.y, 2)
                + pow(locations.z - user.z, 2)
            ) / (2 * 6371000.785)
        ) AS distance
    FROM locations, user
    HAVING distance < 2000 
    ORDER BY distance ASC
选择位置。*,
2 * 6371000.785 *
阿辛(
sqrt(
pow(locations.x-user.x,2)
+pow(locations.y-user.y,2)
+pow(locations.z-user.z,2)
) / (2 * 6371000.785)
)作为距离
从位置、用户
距离小于2000的
按距离订购ASC

如果您需要的不是“米”,则必须将地球半径更改为约6371000.785(米)无论您需要什么,也可以将所需的2000距离更改为您喜欢的距离,或将其存储在每个用户的用户表中。

2的第二个缺点是,每当添加新事件/客户时,您必须维护或重新生成该列表。@xQbert您说得对,我没有说得足够清楚。我再加一点。