django - 在 PostgreSQL(PostGIS) 中优化 ST_Intersects

标签 django postgresql query-optimization postgis postgresql-9.3

下面的查询需要将近 15 分钟的时间才能显示结果。我想知道为什么?因为数据?还是几何体的顶点?当我尝试使用不同的表(小型 shapefile)进行查询时,它运行得很快。

这是查询。 (为此感谢 Patrick):

WITH hi AS (
  SELECT ps.id, ps.brgy_locat, ps.municipali
  FROM evidensapp_polystructures ps
  JOIN evidensapp_seniangcbr fh ON fh.hazard = 'High'
                                 AND ST_Intersects(fh.geom, ps.geom)
), med AS (
  SELECT ps.id, ps.brgy_locat, ps.municipali
  FROM evidensapp_polystructures ps
  JOIN evidensapp_seniangcbr fh ON fh.hazard = 'Medium'
                                 AND ST_Intersects(fh.geom, ps.geom)
  EXCEPT SELECT * FROM hi
), low AS (
  SELECT ps.id, ps.brgy_locat, ps.municipali
  FROM evidensapp_polystructures ps
  JOIN evidensapp_seniangcbr fh ON fh.hazard = 'Low'
                                 AND ST_Intersects(fh.geom, ps.geom)
  EXCEPT SELECT * FROM hi
  EXCEPT SELECT * FROM med
)
SELECT brgy_locat AS barangay, municipali AS municipality, high, medium, low
FROM (SELECT brgy_locat, municipali, count(*) AS high
      FROM hi
      GROUP BY 1, 2) cnt_hi
FULL JOIN (SELECT brgy_locat, municipali, count(*) AS medium
      FROM med
      GROUP BY 1, 2) cnt_med USING (brgy_locat, municipali)
FULL JOIN (SELECT brgy_locat, municipali, count(*) AS low
      FROM low
      GROUP BY 1, 2) cnt_low USING (brgy_locat, municipali);

PostgreSQL 9.3、PostGIS 2.1.5

Polystructures:包含 9847 行:

CREATE TABLE evidensapp_polystructures (
  id serial NOT NULL PRIMARY KEY,
  bldg_name character varying(100) NOT NULL,
  bldg_type character varying(50) NOT NULL,
  brgy_locat character varying(50) NOT NULL,
  municipali character varying(50) NOT NULL,
  province character varying(50) NOT NULL,
  geom geometry(MultiPolygon,32651)
);

CREATE INDEX evidensapp_polystructures_geom_id
  ON evidensapp_polystructures USING gist (geom);
ALTER TABLE evidensapp_polystructures CLUSTER ON evidensapp_polystructures_geom_id;

SeniangCBR:只有 6 行,shapefile 大小(如果重要的话):52,060 KB

CREATE TABLE evidensapp_seniangcbr (
  id serial NOT NULL PRIMARY KEY,
  hazard character varying(16) NOT NULL,
  geom geometry(MultiPolygon,32651)
);

CREATE INDEX evidensapp_seniangcbr_geom_id ON evidensapp_seniangcbr USING gist (geom);
ALTER TABLE evidensapp_seniangcbr CLUSTER ON evidensapp_seniangcbr_geom_id;

使用LayerMapping 自动将所有数据加载到数据库中我正在使用的实用程序 Django(GeoDjango) .

EXPLAIN ANALYZE LINK HERE.

我现在没有服务器,我在我的电脑上运行查询。

  • 处理器:Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz(8 个 CPU),~3.6GHz
  • 内存:8192MB RAM
  • 操作系统:Windows 7 64 位

最佳答案

EXPLAIN ANALYZE 输出很难阅读,因为所有字段和函数都被打乱到 radio alphabet 中.也就是说,有两点很突出:

  1. 大部分时间花在 ST_Intersects() 函数上,这不足为奇。
  2. EXCEPT 子句似乎也相当低效。

所以请尝试这个更简洁的版本:

SELECT brgy_locat AS barangay, municipali AS municipality,
       sum(CASE max_hz_id WHEN 3 THEN 1 ELSE 0 END) AS high,
       sum(CASE max_hz_id WHEN 2 THEN 1 ELSE 0 END) AS medium,
       sum(CASE max_hz_id WHEN 1 THEN 1 ELSE 0 END) AS low
FROM (
  SELECT ps.id, ps.brgy_locat, ps.municipali,
         max(CASE fh.hazard WHEN 'Low' THEN 1 WHEN 'Medium' THEN 2 WHEN 'High' THEN 3 END) AS max_hz_id
  FROM evidensapp_polystructures ps
  JOIN evidensapp_seniangcbr fh ON ST_Intersects(fh.geom, ps.geom)
  GROUP BY 1, 2, 3
) AS ps_fh
GROUP BY 1, 2;

现在只有一次对 ST_Intersects() 的调用,这可能(希望)比对危险 map 子集的三个调用快很多(由于 PostGIS 的内部效率代码)。

很明显,危险类别字符串被转换为整数范围,以便于排序和比较。在内部查询中,根据您的要求选择最大危险值。在主查询中,每个结构的那些最大值被汇总到它们各自的列中。如果可能的话,更改您的表结构以使用这三个整数代码并链接到类标签的帮助表:您的表会变得更小,因此更快,并且内部查询中的 CASE 语句可以被丢弃。或者,添加一个包含整数代码的列,并根据“危险”列更新值。

请注意,这些 CASE 语句不是很有效(我在上一个答案中使用 EXCEPT 子句的原因)。在 PG 9.4 中,引入了一个关于聚合函数的新 FILTER 子句,这将使查询更快且更易于阅读:

count(id) FILTER (WHERE max_hz_id = 3) AS high

您可能需要考虑升级。

拉马特穆拉梅尼拉

关于django - 在 PostgreSQL(PostGIS) 中优化 ST_Intersects,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31799824/

相关文章:

mysql - 优化查询 : Select in Select

django - 如何设置 Django 应用程序以使 cookie 在子域上工作

django - 在Django 1.7上的数据迁移中,直到原子 block 结束,才能执行查询

mysql - 克服 LEFT JOIN 中的 "at least once"条件

node.js - Postgres SQL 加入多对多关系

sql - 为什么 union 不能与 with 语句一起使用?

performance - USER_TAB_COLUMNS 中的 LOW_VALUE 和 HIGH_VALUE

python manage.py runserver 抛出错误

python - DJANGO:如何在注册表中排序我的输入内容?

SQL - 如何对表中的某些行求和/聚合