PostgreSQL / PostGIS中最有效的方法是检查100万个线串是否相互交叉?

时间:2016-01-15 15:34:43

标签: performance postgresql postgis

我在表的几何字段中有一百万个字符串,' geom'。我试图找到彼此交叉的线串,以便我可以从原始表中删除它们。 我在' geom'上创建了一个GIST空间索引,并将该表聚集在该索引上。有没有更有效的方法来做这个,不需要进行万亿(10 ^ 6×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。

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

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 ms。

1 个答案:

答案 0 :(得分:0)

使用ST_DWithin您只会与亲近的人进行比较。并将使用索引。我的桌子是500k行,大约需要30秒。

这是我找到一个节点断开道路网络时的例子,我比较了end / begin节点,并检查是否与另一个end / begin节点相交。

所以只能比较接近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秒。我能看到的唯一区别是我将点与线进行比较。

enter image description here

EXPLAIN ANALYZE

"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"