Postgres - 全表扫描速度太慢 - 索引未被使用

Ale*_*s G 2 postgresql optimization

我在postgres数据库中有一个包含许多列的表,其中包括:

n_store_object_id     integer,
n_latitude            decimal,
n_longitude           decimal
Run Code Online (Sandbox Code Playgroud)

该表目前约有250,000行.

我需要找到位于距给定位置固定距离内的非null 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;
Run Code Online (Sandbox Code Playgroud)

现在,我要求的查询很简单:

select *
  from objects
 where n_store_object_id is not null
   and fn_geo_distance(51.5, 0, n_latitude, n_longitude) <= 20
Run Code Online (Sandbox Code Playgroud)

这需要相当长的时间 - 当我"解释"这个查询时,我可以看到全表扫描.很公平.所以我在这三列上创建了一个索引:

create index idx_object_location on objects(n_store_object_id, n_latitude, n_longitude)
Run Code Online (Sandbox Code Playgroud)

我重新运行上面的查询 - 它仍然需要很长时间."解释"它表明没有使用新创建的索引.我错过了什么吗?为什么不使用它,如何强制引擎使用它?哦,首先,这个指数会有帮助吗?

谢谢!

Phi*_*ing 6

您的索引按ID排序,然后是纬度,然后是长.这无济于事,因为它无法找出要搜索的ID范围.

你不能使用传统的"btree"索引(postgres和其他所有sql中的默认索引)对此进行索引.如果您暂时考虑问题,大多数索引都基于订购事物(数字或字母顺序).但你不能订购地理.您可以按照距离单点的距离顺序排序,但是当您移动该点时,某些事情会更接近,而其他事情会更进一步,因此订单会发生变化.

最好... 为此问题创建了特殊索引.由于您使用的是postgres,我建议您阅读GiST. http://postgis.net/docs/manual-2.0/using_postgis_dbmanagement.html(请谷歌以及此链接).

现在作为postgres的一部分包含在内,专门用于处理地理位置.

另外...... 第二种解决方案是在数据上放置两个索引,一个(仅)一个logditude(仅).并在查询中添加max和min lat和long,如另一个答案中所述.Postgres可以使用BOTH索引来缩小范围.重要的是使用两个单独的索引,而不是包含lat和long的索引.