我是地理空间领域和数据的新手,最近需要地理空间搜索,我需要将其作为模型中的功能。这个想法是对于给定的点 A,如何找到 20 英里内最近的 10 个点。根据文档和搜索,我发现了许多地理函数并创建了此查询来搜索并返回最近的点:
INSERT INTO candidate_table (ref_id, longitude, latitude, geom)
SELECT
ref_id,
longitude,
latitude,
ST_POINT(longitude, latitude)
FROM
input
LEFT JOIN
list_of_points USING (ref_id)
;
INSERT INTO subject_table (ref_id, longitude, latitude, geom)
SELECT
ref_id,
longitude,
latitude,
ST_POINT(longitude, latitude)
FROM
input
LEFT JOIN
list_of_points_b USING (ref_id)
;
--it is working
SELECT
s.ref_id AS subject_ref_id,
c.ref_id AS candidate_ref_id,
ST_DISTANCE(s.geom, c.geom) AS distance_mi
FROM subject_table s
JOIN candidate_table c
ON ST_DWITHIN(s.geom, c.geom, 20 * 1609.34)
WHERE s.geom IS NOT NULL AND c.geom IS NOT NULL
ORDER BY s.ref_id, ST_DISTANCE(s.geom, c.geom)
;
然而,这种方法创建的搜索非常慢,比我想象的要慢得多。我知道地理空间索引可以让事情变得更快,而且我知道 Snowflake 支持 h3 索引函数。这是制作速度更快的方法还是您会推荐什么?
首先要获得您的英里距离,您需要将其修正为米。
ST_DISTANCE(s.geom, c.geom)/1609.34 AS distance_mi
好吧,让我们在有效的经纬度范围内构建随机数据,看看我们需要花费多少行来运行“慢速查询”:
-- 100K
create or replace table c_table_100K as
select
seq8() as id,
random()/(9223372036854775807/180) as lon,
random()/(9223372036854775807/90) as lat,
ST_POINT(lon, lat) as geom
from table(generator(ROWCOUNT=>100000));
create or replace table s_table_100K as
select
seq8() as id,
random()/(9223372036854775807/180) as lon,
random()/(9223372036854775807/90) as lat,
ST_POINT(lon, lat) as geom
from table(generator(ROWCOUNT=>100000));
因此使用这些表的 SQL 需要 3.6 秒:
SELECT
s.id AS s_id,
c.id AS c_id,
ST_DISTANCE(s.geom, c.geom)/1609.34 AS distance_mi
FROM s_table_100K as s
JOIN c_table_100K as c
ON ST_DWITHIN(s.geom, c.geom, 20 * 1609.34)
WHERE s.geom IS NOT NULL AND c.geom IS NOT NULL
ORDER BY s.id, ST_DISTANCE(s.geom, c.geom);
删除 ORDER BY,查询需要 3.8 秒,所以它是免费的..或者不是成本 预计算数学(32186.8),优化器也会这样做,3.6 秒,再次免费。
切换为使用计算结果,又名:
SELECT
s.id AS s_id,
c.id AS c_id,
ST_DISTANCE(s.geom, c.geom)/1609.34 AS distance_mi
FROM s_table_100K as s
JOIN c_table_100K as c
ON distance_mi <= 20
WHERE s.geom IS NOT NULL AND c.geom IS NOT NULL
我运行了它,截至 5 分钟,右腿只处理了 100K 行中的 3.8K,因此 CartesianJoin 代码很慢,看起来需要大约 2 小时 19 分钟。我们不要去那里。
查看配置文件中的 3.6 JOIN 时间,我们看到它正在执行 GEO_JOIN,因此我怀疑我们可能很难执行更好的连接,因为距离过滤器很可能用于将数据存储在引擎盖下,分成块然后进行桶匹配。我怀疑我的正常情况,让手滚动这个,只会迷失在民意调查的边缘情况中,所以 ST_DWITHIN 应该被认为是最好的选择,而下一个最好的事情是,因为这些空检查显示的是预过滤任何其他在加入之前可以知道的细节,如“不是我们正在寻找的点”。