【问题标题】:Most efficient method in PostgreSQL / PostGIS to check if 1 million linestrings cross each other?PostgreSQL / PostGIS中检查100万条线串是否相互交叉的最有效方法?
【发布时间】:2016-01-15 15:34:42
【问题描述】:

我在表格的几何字段“geom”中有一百万条线串。我试图找到相互交叉的线串,以便我可以从原始表中删除它们。 我在“geom”上创建了一个 GIST 空间索引,并将该表聚集在该索引上。有没有更有效的方法,不需要进行一万亿次 (10^6 x 10^6) 的比较?

SELECT (CASE WHEN A.length >= B.length THEN A.gid ELSE B.gid END)
INTO lines_self_crossing
FROM lines AS A, lines AS B
WHERE ST_Crosses(A.geom, B.geom) = true
;

我正在清理使用 ST_ShortestLine 在 400,000 个宗地和 40,000 个道路线串之间生成的“线”表。我已经删除了穿过道路或地块的线。该表包含来自原始宗地及其终点的道路的唯一 ID。

编辑 这是重写的查询,并解释分析输出:

explain analyze
SELECT (CASE WHEN A.length >= B.length THEN A.gid ELSE B.gid END)
INTO shortline_crosses_shortline2
FROM parcelstiug_roadciu1mwt_sl_noroadnoparcelcross AS A
JOIN parcelstiug_roadciu1mwt_sl_noroadnoparcelcross AS B
  ON ST_DWithin(A.geom, B.geom, 1) 
  AND ST_Crosses(A.geom, B.geom) = true
WHERE A.gid <> B.gid       
;

解释分析的输出:

`    Nested Loop  (cost=0.29..4273300.12 rows=96 width=24) (actual time=6.111..1692272.505 rows=8363188 loops=1)
      ->  Seq Scan on parcelstiug_roadciu1mwt_sl_noroadnoparcelcross a  (cost=0.00..21795.31 rows=897431 width=76) (actual time=0.008..128.911 rows=897431 loops=1)
      ->  Index Scan using ptiugrciu1mwtslnrnpc_spindex on parcelstiug_roadciu1mwt_sl_noroadnoparcelcross b  (cost=0.29..4.73 rows=1 width=76) (actual time=0.806..1.881 rows=9 loops=897431)
            Index Cond: ((geom && st_expand(a.geom, 1::double precision)) AND (a.geom && geom))
            Filter: ((a.gid <> gid) AND (a.geom && st_expand(geom, 1::double precision)) AND _st_dwithin(a.geom, geom, 1::double precision) AND _st_crosses(a.geom, geom))
            Rows Removed by Filter: 74
    Total runtime: 1696618.617 ms`

EDIT2 索引定义如下:

CREATE INDEX ptiugrciu1mwtslnrnpc_gid
  ON parcelstiug_roadciu1mwt_sl_noroadnoparcelcross
  USING btree
  (gid);

CREATE INDEX ptiugrciu1mwtslnrnpc_parceltayoid
  ON parcelstiug_roadciu1mwt_sl_noroadnoparcelcross
  USING btree
  (parcel_tayoid);

CREATE INDEX ptiugrciu1mwtslnrnpc_roadgid
  ON parcelstiug_roadciu1mwt_sl_noroadnoparcelcross
  USING btree
  (road_gid);

CREATE INDEX ptiugrciu1mwtslnrnpc_spindex
  ON parcelstiug_roadciu1mwt_sl_noroadnoparcelcross
  USING gist
  (geom);
ALTER TABLE parcelstiug_roadciu1mwt_sl_noroadnoparcelcross CLUSTER ON ptiugrciu1mwtslnrnpc_spindex;

EDIT3:使用 ST_Intersects 而不是 ST_Crosses 将运行时间缩短到 343616 毫秒。

【问题讨论】:

    标签: performance postgresql postgis


    【解决方案1】:

    使用ST_DWithin你只会与你身边的人比较。并将使用索引。我的表有 500k 行,大约需要 30 秒。

    这是我的示例,用于查找节点何时与道路网络断开连接,我比较结束/开始节点并检查是否与另一个结束/开始节点相交。

    所以只比较接近 1 米的那个。而不是自己比较。

    我认为0.0001 的值取决于您的预测,但使用ST_Distance 很容易测试。

    SELECT 
          f.id as node_id, 
          g.id as line_id,
          'start_node' as type
    FROM ways_rto f 
    JOIN ways_rto g
      on ST_DWithin(f.sp, g.geom, 0.0001)
     and ST_Intersects(f.sp, g.geom)
     and NOT ( f.sp_txt = g.sp_txt OR f.sp_txt = g.ep_txt)
    WHERE f.id <> g.id
    

    更新:我再次进行了复制数据的测试,时间为 60 秒。我能看到的唯一区别是我将点与线进行比较。

    解释分析

    "Nested Loop  (cost=0.00..10748875.97 rows=1083 width=8) (actual time=13913.424..60016.767 rows=136 loops=1)"
    "  ->  Seq Scan on test_rto g  (cost=0.00..54344.94 rows=1379094 width=140) (actual time=0.298..196.095 rows=1379094 loops=1)"
    "  ->  Index Scan using test_rto_sp_idx on test_rto f  (cost=0.00..7.74 rows=1 width=55) (actual time=0.041..0.041 rows=0 loops=1379094)"
    "        Index Cond: ((sp && st_expand(g.geom, 0.0001::double precision)) AND (sp && g.geom))"
    "        Filter: ((sp_txt <> g.sp_txt) AND (sp_txt <> g.ep_txt) AND (id <> g.id) AND (g.geom && st_expand(sp, 0.0001::double precision)) AND _st_dwithin(sp, g.geom, 0.0001::double precision) AND _st_intersects(sp, g.geom))"
    "        Rows Removed by Filter: 7"
    "Total runtime: 60016.872 ms"
    

    【讨论】:

    • 谢谢 Juan Carlos,在 1/3 米处,这确实减少了几分钟,现在是 30 分钟,而之前是 33 分钟。
    • 你能分享explain analyze吗?看起来你还有其他问题。 1M^2 = 1E12500k^2 is 2.5E11 所以应该只困难 10 倍?您可以尝试制作一个 500k 的较小表来进行比较吗?
    • 我将数据大小增加了一倍,只需要 60 秒,你还有其他东西会减慢这个过程。如您所见,索引完成了大部分工作
    • 请看我添加到原始问题中的解释分析。
    • 是的,我正要建议。您可以尝试使用 ST_Intersect 吗?你能发布如何用索引定义你的表吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 2017-03-29
    • 2014-06-05
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多