0

我正在使用 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 个约束,实际得到结果需要几秒钟。

我知道查询可能看起来很糟糕,但许多限制真的是瓶颈吗?这个查询可以优化吗?

4

6 回答 6

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 列上有一个索引吗?)

于 2009-09-16T09:34:22.920 回答
1

速度的权衡空间:

城市不动。每当您添加一个事件时,您可以预先计算每个事件与每个城市之间的距离,并存储到附近所有城市的距离。您可以按城市对其进行索引,因此您可以直接找到某个特定城市附近的事件(或近 200 个具有相同前缀的城市)。然后可以将实际的经度/纬度过滤限制为一组更小的事件。

于 2009-09-16T15:34:37.487 回答
0

您可以重新设计数据库(如果可能的话),不仅包含纬度和经度,还包含事件地点的名称。您的查询将包含like语句或类似的 ( begins with?)。我知道,这可能是无法使用的解决方案,但是将自己限制在方形(球形意义上)城市或地区对我来说似乎有点奇怪;)

于 2009-09-16T09:26:25.060 回答
0

在 events.lat 和/或 events.long 上创建一个范围搜索友好的索引(B-tree 索引)(但不是两个上的单个索引!)这至少会让你在球场上。

您真正想要的是 R-Tree 或类似的,它允许索引多维数据并为您提供良好的范围搜索性能。PostgreSQL为此提供了GiST;我不知道 Firebird 对这类问题有什么样的支持。

更多信息的 Wiki 链接:http: //en.wikipedia.org/wiki/R-tree http://en.wikipedia.org/wiki/GiST

于 2009-09-16T14:55:35.493 回答
0

您应该首先在查询上使用 IBExpert 来检查它的计划,看看为什么它这么慢。

于 2009-09-16T15:04:47.203 回答
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
)

我在某些情况下它比连接更快。

于 2010-02-04T16:43:45.730 回答