2009-11-06 58 views
2

给定一个表的用户,由于纬度和经度,找到感兴趣的是位置

+-----+---------+---------+---------+---------+---------+ 
| user| min_lat | max_lat | min_lng | max_lng | 
+-----+---------+---------+---------+---------+---------+ 
| a | 46 |  407 |  6 |  367 | 
| b | 226 |  227 |  186 |  188 | 

与点(X,Y)查找用户,其中一点是最小和最大的经度和纬度范围内用户(其中​​min和max long和lat =当前位置减去或加上半径)。

最小值可以小于0,最大值可以大于360,查询需要考虑这些因素。

E.g.使用Point(7,5)进行过滤也应该返回用户A,因为367-360 = 7。

不知道我是否得到这个权利,但希望有人能给我一些见解。

+0

点(4,5)是一个更好的实例中,如图7是在范围6..367(不考虑a == b mod 360)。 4在范围之外,但在a区域之内。此外,每个点都在该地区(6..367,46..407);也许(50..367,46..407)是一个更好的测试区域,因为经度范围排除了一些值,但是纬度没有。此外,这些地区是否包含终点?术语“最小”和“最大”暗示它们是,但最好是明确的。 – outis 2009-11-11 07:30:15

回答

0

要回答你的查询的一部分,如果你说367和7是相同的,我同样假定727也是。

因此,你要使用360的模数。余数除以360.

例如(其中,%是C#和C++的语法,你可能会发现一个SQL的方式来做到这一点)

7 % 360 = 7 
367 % 360 = 7 
727 % 360 = 7 

看起来像SELECT b MOD 360 from table;是你想要的那种东西。

0

我怀疑有一个更优雅的答案,但我认为SQL where子句的限制使得这一点更加困难。

假设:

  • 0 <= x < 360
  • 0 <= y < 360
  • max_lat - min_lat <= 361
  • max_lng - min_lng <= 361
SELECT user FROM user_location WHERE 
((min_lat < 0 AND ((0 <= y AND y <= max_lat) OR (min_lat + 360 <= y AND y < 360))) OR 
(min_lat >= 0 AND ((min_lat <= y AND y <= max_lat) OR (0 <= y AND y <= max_lat - 360)))) 
AND 
((min_lng < 0 AND ((0 <= x AND x <= max_lng) OR (min_lng + 360 <= x AND x <= 360))) OR 
(min_lng >= 0 AND ((min_lng <= x AND x <= max_lng) OR (0 <= x AND x <= max_lng - 360)))) 

如果min> = 0,那么x必须落在min和max之间,或者它必须落在0和max - 360.

为了清楚起见,提供了上述格式。如果将360的加法和减法移到x和y参数中,则比较会变得恒定,并可能显着加快查询速度。

+0

完成使用此,谢谢! – user167206 2009-11-29 21:48:54

0

使用模数来确保所有值都介于0和360之间,并且查询变得非常简单。假设{pointLat}和{pointLng}是被过滤的点的坐标。

SELECT * 
FROM table 
WHERE IF(min_lat < max_lat, 
     MOD({pointLat}, 360) BETWEEN MOD(min_lat, 360) AND MOD(max_lat, 360)), 
     MOD({pointLat}, 360) NOT BETWEEN MOD(min_lat, 360) AND MOD(max_lat, 360))) 
    AND IF(min_lng < max_lng, 
     MOD({pointLng}, 360) BETWEEN MOD(min_lng, 360) AND MOD(max_lng, 360)), 
     MOD({pointLng}, 360) NOT BETWEEN MOD(min_lng, 360) AND MOD(max_lng, 360))); 

虽然这可行,但我强烈建议在SQL查询之外执行MOD计算或添加具有规范化值的额外列。在这段代码中使用MOD函数将阻止查询利用这些列上的任何索引。

+0

如果min = 350,max = 370,point = 2,则比较变成“我在350和10之间”,但我认为它不会匹配。 – 2009-11-08 16:11:41

+0

查询更新以正确处理该案件。 – jonthornton 2009-11-08 20:58:15

+0

现在您有:MOD(370360)和MOD(350,360) 之间MOD(350,360)和MOD(370360)之间的 MOD(2360)... OR MOD(2360)成为 2 350和10之间或 2 10和350之间 哪些仍然不符合标准。 – 2009-11-10 22:47:21

2

我建议将表中存储的纬度&经度值限制在[0,360]的范围内。在插入和更新之前创建触发器,以强制执行mod 360等效,如果max-min> 360,则分别将最小值和最大值分别设置为0和360。例如:

delimiter ;; 

CREATE TRIGGER normalize_inserted_ranges BEFORE INSERT 
    ON table 
    FOR EACH ROW BEGIN 
    IF NEW.max_lat - NEW.min_lat >= 360 THEN 
     SET NEW.min_lat=0; 
     SET NEW.max_lat=360; 
    ELSE 
     SET NEW.min_lat = NEW.min_lat % 360; 
     SET NEW.max_lat = NEW.max_lat % 360; 
    END IF; 
    IF NEW.max_lng - NEW.min_lng >= 360 THEN 
     SET NEW.min_lng=0; 
     SET NEW.max_lng=360; 
    ELSE 
     SET NEW.min_lng = NEW.min_lng % 360; 
     SET NEW.max_lng = NEW.max_lng % 360; 
    END IF; 
    END 
;; 
delimiter ; 

然后可以使用以下查询:

SELECT user FROM table 
    WHERE 
     IF(min_lng <= max_lng, 
     @x BETWEEN min_lng AND max_lng, 
     @x <= max_lng OR min_lng <= @x) 
    AND 
     IF(min_lat <= max_lat, 
     @y BETWEEN min_lat AND max_lat, 
     @y <= max_lat OR min_lat <= @y) 
; 
+0

我喜欢你的解决方案。我的一个担忧是你失去了信息。从最初的问题来看很难说最终的意图是什么,但似乎改变价值观可能会抹去有价值的信息。我认为很容易保留原始数据并存储修改过的数据以便以更快的搜索速度来牺牲额外的存储空间。干得不错! – 2009-11-13 07:46:49

+0

有关其他数据使用问题的(缺少)要求留下一些空间来更改存储表示。关于将这两种形式作为时间/空间折衷存储的关键点。 – outis 2009-11-15 04:27:27