【发布时间】:2021-01-10 01:04:29
【问题描述】:
我有一个用户表,每个用户都有纬度和经度。我有一个类似“距离容差”的东西,每个用户都可以定义。考虑这一点的一种简单方法是将其构建为约会应用程序:
Alice will only date users within 30km
使用 PostGIS,使用用户位置的 GIST 索引来设置查找附近用户的查询并不难。这里概述了这种方法:https://ngauthier.com/2013/08/postgis-and-rails-a-simple-approach.html,但这里是基础知识:
=> CREATE EXTENSION postgis;
=> CREATE TABLE users (
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
latitude numeric(9,6),
longitude numeric(9,6),
distance int DEFAULT 20000
);
=> INSERT INTO users (latitude, longitude, distance) (
SELECT
40.7128 + x.lat AS latitude,
-74.006 + x.lon AS longitude,
10000 * (1 + floor(random() * 3)) AS distance
FROM (
SELECT random() * 2 as lat, random() * 2 as lon
FROM generate_series(1,1000000)
) AS x);
=> CREATE INDEX index_on_users_location ON users
USING gist (st_geographyfromtext((((('SRID=4326;POINT('::text
|| longitude) || ' '::text) || latitude) || ')'::text)));
此时,根据 Alice 的偏好查询附近的用户就很容易了:
=> SELECT COUNT(*) FROM users WHERE
ST_DWithin(ST_GeographyFromText('SRID=4326;POINT(' ||
users.longitude || ' ' || users.latitude || ')'),
ST_GeographyFromText('SRID=4326;POINT(' ||
'-74.006 40.7128)'),
30000);
Postgres 使用索引并查找行。问题是,如果您还想考虑 Bob 的偏好,您需要使用两者的最小距离来评估两个用户是否足够接近。
Alice will only date users within 30km
Bob will only date users within 60km
我的(天真的)查询现在是:
=> SELECT COUNT(*) FROM users WHERE
ST_DWithin(ST_GeographyFromText('SRID=4326;POINT(' ||
users.longitude || ' ' || users.latitude || ')'),
ST_GeographyFromText('SRID=4326;POINT(' ||
'-74.006 40.7128)'),
LEAST(users.distance, 30000));
此时,Postgres 决定索引不再有用,并切换到顺序扫描表。这大概是因为我需要比较每一行并使用距离容差较小的人。有没有办法可以存储点旁边的距离?也许使用btree_gist?我相对确定我不能从文档中“包括”旁边的距离......
...非键列不能用于索引扫描搜索限定...
【问题讨论】:
标签: postgresql postgis