sql - PostgreSQL:查询无法使用计数

标签 sql postgresql group-by

WITH hi AS (
  SELECT ps.id, ps.brgy_locat, ps.municipali, ps.bldg_name, fh.gridcode, ps.bldg_type
  FROM evidensapp_polystructures ps
  JOIN evidensapp_floodhazard fh ON fh.gridcode=3
                                 AND ST_Intersects(fh.geom, ps.geom)
), med AS (
  SELECT ps.id, ps.brgy_locat, ps.municipali ,ps.bldg_name, fh.gridcode, ps.bldg_type
  FROM evidensapp_polystructures ps
  JOIN evidensapp_floodhazard fh ON fh.gridcode=2
                                 AND ST_Intersects(fh.geom, ps.geom)
  EXCEPT SELECT * FROM hi
), low AS (
  SELECT ps.id, ps.brgy_locat, ps.municipali,ps.bldg_name, fh.gridcode, ps.bldg_type
  FROM evidensapp_polystructures ps
  JOIN evidensapp_floodhazard fh ON fh.gridcode=1
                                 AND ST_Intersects(fh.geom, ps.geom)
  EXCEPT SELECT * FROM hi
  EXCEPT SELECT * FROM med
)
SELECT brgy_locat, municipali, bldg_name,  bldg_type, gridcode, count( bldg_name)
FROM (SELECT brgy_locat, municipali, bldg_name, gridcode, bldg_type
      FROM hi
      GROUP BY 1, 2, 3, 4, 5) cnt_hi
FULL JOIN (SELECT brgy_locat, municipali,bldg_name, gridcode, bldg_type
      FROM med
      GROUP BY 1, 2, 3, 4, 5) cnt_med USING (brgy_locat, municipali, bldg_name,gridcode,bldg_type)
FULL JOIN (SELECT brgy_locat, municipali,bldg_name,gridcode, bldg_type
      FROM low
      GROUP BY 1, 2, 3, 4, 5) cnt_low USING (brgy_locat, municipali, bldg_name, gridcode, bldg_type)

上面的查询返回一个错误:

ERROR: column "cnt_hi.brgy_locat" must appear in the GROUP BY clause or be used in an aggregate function ********** Error **********

ERROR: column "cnt_hi.brgy_locat" must appear in the GROUP BY clause or be used in an aggregate function SQL state: 42803

但是如果我省略 count(bldg_name) 它会起作用。但我需要根据 bldg_name 进行计数。

编辑: 我想获得与危险值(网格代码)相交的建筑物数量:高(3)、中(2)和低(1)。但是,如果某个几何图形已经在 High 中相交,则在其中排除 Medium 查询,Low 也会排除那些在 High 和 Medium 中相交的几何图形。

PostgreSQL:9.4,PostGIS:2.1.7

表详细信息:

CREATE TABLE evidensapp_floodhazard (
  id integer NOT NULL DEFAULT nextval('evidensapp_floodhazard_id_seq'::regclass),
  gridcode integer NOT NULL,
  date_field character varying(60),
  geom geometry(MultiPolygon,32651),
  CONSTRAINT evidensapp_floodhazard_pkey PRIMARY KEY (id)
);

CREATE INDEX evidensapp_floodhazard_geom_id
  ON evidensapp_floodhazard USING gist (geom);

ALTER TABLE evidensapp_floodhazard CLUSTER ON evidensapp_floodhazard_geom_id;

CREATE TABLE evidensapp_polystructures (
  id serial NOT NULL,
  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),
  CONSTRAINT evidensapp_polystructures_pkey PRIMARY KEY (id)
);

CREATE INDEX evidensapp_polystructures_geom_id
  ON evidensapp_polystructures USING gist (geom);

ALTER TABLE evidensapp_polystructures CLUSTER ON evidensapp_polystructures_geom_id;

预期的输出是这样的,但计数正确: enter image description here

编辑 2: 无论如何,尽管我尽力解释预期的输出是什么:

  • 计算 bldg_name 而不是 id 它在 floodhazard 中与 floodhazard 相交的网格代码,条件如上所述 EDIT 1.
  • 然后将其分组到 brgy_locatbrgy_municipali 以及它所属的 gridcodebldg_type

请看上面的图片。

最佳答案

你可能想要这个:

WITH hi AS (
   SELECT ps.brgy_locat, ps.municipali, ps.bldg_name, ps.bldg_type, fh.gridcode
        , count(*) OVER(PARTITION BY ps.bldg_name, ps.bldg_type) AS building_count
   FROM   evidensapp_polystructures ps
   JOIN   evidensapp_floodhazard    fh ON fh.gridcode = 3
                                      AND ST_Intersects(fh.geom, ps.geom)
   )
, med AS (
   SELECT ps.brgy_locat, ps.municipali, ps.bldg_name, ps.bldg_type, fh.gridcode
        , count(*) OVER(PARTITION BY ps.bldg_name, ps.bldg_type) AS building_count
   FROM   evidensapp_polystructures ps
   JOIN   evidensapp_floodhazard    fh ON fh.gridcode = 2
                                      AND ST_Intersects(fh.geom, ps.geom)
   LEFT   JOIN hi USING (bldg_name, bldg_type)
   WHERE  hi.bldg_name IS NULL
   )
TABLE hi

UNION ALL
TABLE med

UNION ALL 
   SELECT ps.brgy_locat, ps.municipali, ps.bldg_name, ps.bldg_type, fh.gridcode
        , count(*) OVER(PARTITION BY ps.bldg_name, ps.bldg_type) AS building_count
   FROM   evidensapp_polystructures ps
   JOIN   evidensapp_floodhazard    fh ON fh.gridcode = 1
                                      AND ST_Intersects(fh.geom, ps.geom)
   LEFT   JOIN hi USING (bldg_name, bldg_type)
   LEFT   JOIN med USING (bldg_name, bldg_type)
   WHERE  hi.bldg_name IS NULL
   AND    med.bldg_name IS NULL;

根据您对问题和聊天的评论,这现在按 (bldg_name, bldg_type) 计算 - 不包括已经在更高级别相交的建筑物 - 再次基于在 (bldg_name, bldg_type) 上。

所有其他列要么是不同的(idgeom),要么是功能相关的计数噪声(brgy_locatmunicipali,...)。 如果不是,请添加更多列 PARTITION BY 子句以消除建筑物歧义。并将相同的列添加到 JOIN 条件的 USING 子句中。

如果建筑物与 evidensapp_floodhazard 中的多行相交且相同 gridcode 它被计算为那么多次。参见替代打击。

由于您实际上并不想聚合行而只是对分区进行计数,关键特性是使用 count() 作为 window function ,而不是像原来那样的聚合函数。基本解释:

count(*) 在这里做得更好:

使用 LEFT JOIN/IS NULL 而不是 EXCEPT。详情:

而且我没能在外部查询中看到 FULL JOIN 的目的。使用 UNION ALL 代替。

替代查询

无论它在同一网格代码级别上与 evidensapp_floodhazard 相交多少次,这都算作一次

此外,此变体(与第一个变体不同!)假设相同 (bldg_name, bldg_type) 的所有行在相同的网格代码级别上匹配,情况可能是也可能不是:

SELECT brgy_locat, municipali, bldg_name, bldg_type, 3 AS gridcode
     , count(*) OVER(PARTITION BY bldg_name, bldg_type) AS building_count
FROM   evidensapp_polystructures ps
WHERE  EXISTS (
   SELECT 1 FROM evidensapp_floodhazard fh
   WHERE  fh.gridcode = 3 AND ST_Intersects(fh.geom, ps.geom)
   )

UNION ALL
SELECT brgy_locat, municipali, bldg_name, bldg_type, 2 AS gridcode
     , count(*) OVER(PARTITION BY bldg_name, bldg_type) AS building_count
FROM   evidensapp_polystructures ps
WHERE  EXISTS (
   SELECT 1 FROM evidensapp_floodhazard fh
   WHERE  fh.gridcode = 2 AND ST_Intersects(fh.geom, ps.geom)
   )
AND    NOT EXISTS (
   SELECT 1 FROM evidensapp_floodhazard fh
   WHERE  fh.gridcode > 2  -- exclude matches on **all** higher gridcodes
   AND    ST_Intersects(fh.geom, ps.geom)
   )

UNION ALL 
SELECT brgy_locat, municipali, bldg_name, bldg_type, 1 AS gridcode
     , count(*) OVER(PARTITION BY bldg_name, bldg_type) AS building_count
FROM   evidensapp_polystructures ps
WHERE  EXISTS (
   SELECT 1 FROM evidensapp_floodhazard fh
   WHERE  fh.gridcode = 1 AND ST_Intersects(fh.geom, ps.geom)
   )
AND    NOT EXISTS (
   SELECT 1 FROM evidensapp_floodhazard fh
   WHERE  fh.gridcode > 1 AND ST_Intersects(fh.geom, ps.geom)
   );

还展示了一个没有 CTE 的变体,它的性能可能会更好,也可能不会更好,具体取决于数据分布。

索引

gridcode 添加到索引可能 会提高性能。 (未使用 PostGis 进行测试):

您需要首先为此安装附加模块 btree_gist。详情:

CREATE INDEX evidensapp_floodhazard_geom_id
  ON evidensapp_floodhazard USING gist (gridcode, geom);

关于sql - PostgreSQL:查询无法使用计数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31845829/

相关文章:

algorithm - Scala:如何通过分组(或分箱)获取 Iterable 的前 N ​​个元素

c# - 我的 Windows 服务实例需要一个贡献函数

mysql - 通过 SSH 隧道访问 SQL

PostgreSQL 错误 :more than one row returned by a subquery used as an expression

php - SQLite : GROUP BY without Aggregate

SQL 当属性至少匹配一个值列表时选择一个组

sql - 在 oracle 中划分日期范围

mysql - 需要帮助将数据集从多行转换为单行

具有不区分大小写搜索的 Hibernate + Postgresql

json - Postgres 9.4 中 JSON -> JSONB 列转换的大致速度