我正在使用Firebird并创建了一个名为EVENTS的表。列是:
id (INT) | name (VARCHAR) | category (INT) | website (VARCHAR) | lat (DOUBLE) | lon (DOUBLE)
现在,用户想要搜索他周围某个半径范围内的事件,但只输入他所在城市的两三个字母。所以我们得到 - 让我们说 - 200个可能的城市,他们的纬度和经度。所以我的SQL查询看起来像:
SELECT id FROM events WHERE ((lat BETWEEN 30.09 AND 30.12) AND (lon BETWEEN 40.78 AND 40.81)) OR ((lat BETWEEN 30.09 AND 30.12) AND (lon BETWEEN 40.78 AND 40.81)) OR ...
因此我们在WHERE子句中得到200个约束,实际得到结果需要几秒钟。
我知道查询可能看起来很糟糕。但是,许多限制是否真的成为瓶颈?可以优化此查询吗?
答案 0 :(得分:2)
我的猜测是数据库引擎决定该标准可能会返回很多行,所以它错误地完全扫描了表。提示它做正确的事情,或执行某种形式的查询重写,例如(可能会或可能没有帮助)
SELECT id
FROM cities c
JOIN events e ON (e.lat BETWEEN c.lat - .01 AND c.lat + .01) AND (e.lon BETWEEN c.lon - .01 AND c.lon + .01)
WHERE c.name LIKE 'x%'
在SQL Server中,您可以编写
SELECT id
FROM cities c
INNER LOOP JOIN events e ON (e.lat BETWEEN c.lat - .01 AND c.lat + .01) AND (e.lon BETWEEN c.lon - .01 AND c.lon + .01)
WHERE c.name LIKE 'x%'
确保正确的计划(你在lat和lon列上有一个索引吗?)
答案 1 :(得分:1)
速度的权衡空间:
城市不动。每当您添加活动时,您都可以预先计算每个活动与每个城市之间的距离,并将距离存储到所有附近城市。您可以按城市对此进行索引,因此您可以直接查找某个城市附近的事件(或具有相同前缀的近200个城市)。然后,可以将实际经度/纬度过滤限制为更小的事件集。
答案 2 :(得分:0)
您可以重新设计数据库(如果可能),不仅包含纬度和经度,还包含事件地点的名称。您的查询将包含like
语句或类似语句(begins with
?)。我知道,这可能是无法使用的解决方案,但是将自己限制在方形(球面意义上)的城市或地区对我来说似乎有些奇怪;)
答案 3 :(得分:0)
在events.lat和/或events.long上创建范围搜索友好索引(B树索引)(但两者上都没有单个索引!)这至少会让你进入大球场。
您真正想要的是R-Tree或类似物,它允许索引多维数据并为您提供良好的搜索范围。 PostgreSQL有GiST为此;我不知道Firebird对这类问题有什么样的支持。
Wiki链接了解更多信息: http://en.wikipedia.org/wiki/R-tree http://en.wikipedia.org/wiki/GiST
答案 4 :(得分:0)
您应首先在查询中使用IBExpert来检查它的计划,看看它为何如此慢。
答案 5 :(得分:0)
尝试使用相关子查询:
select *
from events e
where exists
( select *
from cities c
where c.name like 'X%' and
e.lat BETWEEN c.lat - .01 AND c.lat + .01 and
e.lon BETWEEN c.lon - .01 AND c.lon + .01
)
在某些情况下,它比连接更快。