我在 postgres 数据库中有一个包含许多列的表,其中我有:
n_store_object_id integer,
n_latitude decimal,
n_longitude decimal
该表目前大约有 250,000 行。
我需要找到距离给定位置固定距离内具有非空 store_object_id 的记录。对于距离计算,我有以下功能:
CREATE OR REPLACE FUNCTION fn_geo_distance(numeric, numeric, numeric, numeric)
RETURNS numeric AS
$BODY$
declare
lat1d ALIAS for $1;
lon1d ALIAS for $2;
lat2d ALIAS for $3;
lon2d ALIAS for $4;
lat1 DECIMAL := lat1d / 57.29577951;
lon1 DECIMAL := lon1d / 57.29577951;
lat2 DECIMAL := lat2d / 57.29577951;
lon2 DECIMAL := lon2d / 57.29577951;
begin
return 3963.0 * acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1));
end;$BODY$
LANGUAGE plpgsql IMMUTABLE;
现在,我需要的查询很简单:
select *
from objects
where n_store_object_id is not null
and fn_geo_distance(51.5, 0, n_latitude, n_longitude) <= 20
这需要相当长的时间 - 当我“解释”这个查询时,我可以看到全表扫描。很公平。所以我在这三列上创建了一个索引:
create index idx_object_location on objects(n_store_object_id, n_latitude, n_longitude)
我重新运行上面的查询 - 仍然需要很长时间。 “解释”表明新创建的索引没有被使用。我错过了什么吗?为什么不使用它,如何强制引擎使用它?哦,首先,这个索引有帮助吗?
谢谢!
最佳答案
您的索引按 ID 排序,然后是纬度,然后是经度。这无济于事,因为它无法找出要搜索的 ID 范围。
您不能使用传统的“btree”索引(postgres 和所有其他 sql 中的默认索引)很好地索引。如果您稍微考虑一下这个问题,就会发现大多数索引都是基于对事物进行排序(按数字或字母顺序)的。但你不能订购地理。您可以按照事物与一个点的距离排序,但当您移动该点时,有些事物会更近,有些会更远,因此顺序会发生变化。
最佳... 针对此问题创建了特殊索引。由于您使用的是 postgres,我建议您阅读 GiST。 http://postgis.net/docs/manual-2.0/using_postgis_dbmanagement.html (请谷歌搜索并点击此链接)。
这现在作为 postgres 的一部分包含在内,专为处理地理问题而设计。
或者…… 第二种解决方案是在数据上放置两个索引,一个纬度(仅)和一个对数(仅)。并如另一个答案中所述向查询添加最大和最小纬度和经度。 Postgres 可以同时使用两个索引来缩小范围。重要的是您使用两个单独的索引,而不是一个同时包含纬度和经度的索引。
关于postgresql - Postgres - 全表扫描太慢 - 未使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8448811/