2011-12-08 97 views
1

不知道如何建立这样的开放给专家:查询距离

我有一个客户列表中的客户正在寻找他们的位置半径内的事件。我可以存储他们的邮政编码(或lat/lng)以及他们将参加活动的最大距离。因此,列lat,lng,距离(例如,lat = '22.7447858',lng ='-82.1398589',距离= 25)。

整天发布活动并存储其zipcode/lat/lng。

我想运行一个查询(每天一次)获取事件的客户。我在查看Cyber​​Junkies后Mysql within distance query,但问题是,我正在运行查询相反的方向。我需要找到那些“圈子距离”涵盖当前事件的客户,而不是相反。不知道如何存储圆距(3列以上是否足够好或有更好的方法来存储这种类型的查询的数据)?不确定如何为每个事件查询客户。

在此先感谢!

回答

2

我觉得有这样做的两种主要方法是:在运行计算距离,并预先计算距离一次,然后将它们存储在查找表中。

选项1,即时计算。 Tom van der Woerdt的回答很好地解释了你将如何做到这一点。伪代码查询是这样的:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance 

选项2,预先计算所有的距离。您将创建一个表格(在此示例中称为distance),该表格存储每个客户与每个事件之间的距离。它将有三列:customerid,eventidmiles(或任何你想要的距离度量)。循环查看每位顾客计算每个活动的距离并将每个活动存储在distance中。每次添加新的客户或活动时,都会将相应的记录添加到distance表中。一旦这种结构是到位,调查活动将是简单的:

SELECT * FROM distance WHERE miles < [[some number you pick]] 

那么哪一个更好?这是CPU时间和磁盘空间之间的折中,所以答案取决于您的资源。选项1(即时计算)将需要DBMS进行更多的工作(更多的CPU时间)。随着人员和事件的增加,该查询将花费更长的时间运行。选项2(预先计算的距离)将使查找速度非常快,但折衷是必须将所有这些预先计算的距离存储在磁盘上。你还需要努力确保你的查询表是最新的。每当客户或事件被添加,删除或其经纬度发生改变时,您都需要相应地更新查找表。 Triggers可以帮助您自动完成此过程;只要确保你尝试测试每个场景(添加,删除,移动)以确保查找表得到了它应有的更新。

简短回答:如果您的数据库负载非常轻或者磁盘空间有限,选择选项1(即时计算)。如果您的负载较重但磁盘空间充裕,请选择选项2。选项2是更可能的情况,它更具可扩展性。

+0

对于2来说,第二个缺点就是无论何时添加新的事件/客户,您都必须维护或重新生成该列表。 – xQbert

+0

@xQbert你说得对,我没有说清楚。我会再补充一点。 – ean5533

0

你想要的是:

SELECT * FROM customer, event WHERE (<calc distance>) < customer.distance 

这将只是获得所有客户的所有事件,将它们组合在一起,以获得所有可能的组合(100个客户和10个事件给出了1000名的组合),然后检查他们是否”重新在范围内。 *

我个人建议制作一个DISTANCE(customer,event)函数来为你计算它。以这种方式管理查询比较容易,您可以重新使用它。

* 不一定按照这个顺序

0

从A点到B点的距离将是一样的距离B点到A点(除非你在处理道路的方向和不同的路径) 。

基本上,你会做(在SQL伪代码)

SELECT distance(event_loc, user_loc) <= user_max_distance 
0

如果你的距离计算类似于那些在this solution,那么你可以做这样的事情:

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 
0

除了你可能会考虑使用笛卡尔坐标(x,y和z)其他答案而不是用于db存储的纬度/经度,因为生成的查询表达式在数据库服务器上的加载/时间比纬度/经度距离的可能查询更简单。

一个PHP实现的例子下可以找到:

http://headers-already-sent.com/geodistance/

的方法“getCartesian”将纬度/经度转换成直角坐标系和方法“getDistanceByCartesian”说明了如何计算实际距离。你需要做的是将这个距离计算从PHP转移到SQL查询中(这应该不那么复杂)。

编辑,因为我发现给一个更实际的例子

基于类,你可以在上面的链接我设置了2演示表为我公司的地点和所有MC多纳尔兹餐厅下找到时间在我们的周围,并转换纬度/经度从谷歌地图笛卡尔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); 

基于这两个表的SQL查询找到一定的距离(2000内的所有位置(餐馆),在米在这个例子)到每个用户(我们公司的办公室)将是:

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 

如果你需要比“米”比你将不得不改变约为地球半径的东西。 6371000.785(以米为单位)以满足您的需要,并且还可以将所需的2000年的距离更改为您喜欢的或存储在用户表格中的每个用户的任何内容。