通过空间功能限制行


9

我正在尝试提高以下查询的性能。无论我如何编写查询(FROM子句中的子查询,WHERE子句中的子查询),postgres都坚持通过昂贵的ST_DWITHIN函数运行所有〜570K行,即使County = 24的行只有60行。在运行postgis函数之前,如何才能使postgres在County = 24上进行过滤,这对我来说似乎更快,更高效?700ms并不是引起太多关注的原因,但是随着该表增长到10M +,我开始关注性能。

还要注意,p.id是主键,p.zipcode是fk索引,z.county是fk索引,p.geom具有GiST索引。

查询:

EXPLAIN ANALYZE
  SELECT count(p.id)
  FROM point AS p
  LEFT JOIN zipcode AS z
    ON p.zipcode = z.zipcode
  WHERE z.county = 24
    AND ST_DWithin(
      p.geom, 
      ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269), 
      16090.0,
      false
    )

说明分析:

Aggregate  (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
  ->  Hash Join  (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
        Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
        ->  Seq Scan on point p  (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
              Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
              Rows Removed by Filter: 557731
        ->  Hash  (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
              Buckets: 1024  Batches: 1  Memory Usage: 3kB
              ->  Bitmap Heap Scan on zipcode z  (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
                    Recheck Cond: (county = 24)
                    Heap Blocks: exact=39
                    ->  Bitmap Index Scan on fki_zipcode_county_foreign_key  (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
                          Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms

也许尝试将行“将p指向左为zip联接,将z更改为”,将行“ p指向左为z联接(SELECT * FROM邮政编码WHERE zipcode.county = 24)作为z”?
weiji14年

刚刚尝试过,结果相同。当我自己将pointCounty = 24的〜60行全部复制到新表中时,查询只需要0.453毫秒(相比724毫秒),因此肯定有很大的不同。
乔什(Josh)

1
您应该将其count(*)用作样式问题。id如您所说,如果是pkid,则NOT NULL表示它们相同。除了count(id)具有您必须询问该问题是否id可为空的缺点。
埃文·卡罗尔

1
请问为什么要使用左外部联接?尝试将其更改为内部
联接

如果z.country是限制因素,建议您首先将其放在CTE查询中,然后再检查那些结果是否与您的兴趣点相交。由于在这种情况下,空间指数的选择性可能不如郡县= 24,因此它只是一个障碍。
约翰·鲍威尔

Answers:


3

您可以看到预期的行数与实际的行数有关的问题。计划者认为有7,437行,但是只有63行。统计信息已关闭。同样有趣的是,它不使用边界框索引(索引)搜索,DWithin您可以粘贴的结果\d point。什么版本的PostGIS和PostgreSQL?

尝试跑步ANALYZE point。将条件上移会得到相同的计划吗?

JOIN zipcode AS z
  ON p.zipcode = z.zipcode
  AND z.county = 24

我确实进行了分析,还尝试了在ON的新AND条件,但仍获得700ms的运行时间。这是PGSQL 9.4和PostGIS 2.2。
乔什(Josh)

2

作为一个方面说明,有一个合理的机会,这种行为是在PostGIS的2.3.0修改,如果你想将它的错误。

来自PostgreSQL上文档

一个正数,给出该函数的估计执行成本,以cpu_operator_cost为单位。如果函数返回一个集合,则这是返回的每一行的成本。如果未指定成本,则C语言和内部功能的单位为1个单位,所有其他语言的功能的单位为100个单位。较大的值会使计划人员尝试避免不必要地评估函数。

因此,默认成本为1(非常便宜)。D_Within使用GIST索引非常便宜。但是,该值增加到了100(通过内部代理_ST_DWithin)。

我本人不是CTE方法的忠实拥护者。CTE是优化的围栏。因此,以这种方式执行此操作将为将来的优化消除一些潜在的空间。如果没有更好的默认设置,我宁愿升级。最终,我们必须完成工作,并且该方法显然对您有效。


1

多亏了约翰·鲍威尔(John Powell)的提示,我修改了查询以将县限制条件放入with / CTE查询中,并将性能提高到222ms vs.700。与数据保存在.74 ms时相差甚远。自己的桌子。我仍然不确定为什么计划程序在运行昂贵的postgis函数之前不限制数据集,并且当我拥有较大的数据集时,我将不得不尝试使用它们,但是目前看来,这是解决这种独特情况的一种方法。

with points as (
   select p.id, p.geom from point p inner join zipcode z
   on p.zipcode = z.zipcode
   where county = 24
   ) 


SELECT count(points.id)
FROM points
WHERE ST_DWITHIN(points.geom, (ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269)), 16090.0, false)

1
我们将必须查看所有三个查询计划以及表的架构(在我的答案\ d点中要求)。
埃文·卡罗尔

0

您应该在上创建一个索引zipcode(county, zipcode),该索引应该只对z进行扫描。

您可能还需要尝试使用btree_gist扩展来创建point(zipcode, geom)index或point(geom, zipcode)and zipcode(zipcode, county)index。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.