将总和列添加到 PostgreSQL 9.0 中的 crosstab() 查询

dan*_*lin 2 postgresql datatypes cte pivot postgresql-9.0

继我之前的问题之后:

在 PostgreSQL 9.0 中创建 crosstab() 数据透视表

我设法创建了一个数据透视表来ageband使用该crosstab()函数。我可以使用它来创建基本无几何表的视图或表。

但是,这仍然没有多大用处,因为我需要将其链接到gazetteers_and_addresses.unit_postcode表格以分配几何图形以进行进一步分析。

我将附上两个表的表结构和用于创建交叉表的原始代码。

CREATE OR REPLACE VIEW adult_social_care.vw_ageband AS (
    SELECT * FROM crosstab(
        'SELECT postcode_nospace_, ageband, count(ageband) as total_count
         FROM adult_social_care.activities_in_localities_asc
         GROUP BY postcode_nospace_, ageband
         ORDER BY postcode_nospace_'

         ,$$VALUES ('18-24'::text), ('25-34'), ('35-44'), ('45-54'), ('55-64'), ('65-74'), ('75-84'), ('85-94'), ('95 AND OVER')$$)
    AS ct("postcode" text, "18-24" numeric, "25-34" numeric,"35-44" numeric, "45-54" numeric, "55-64" numeric, "65-74" numeric, "75-84" numeric, "85-94" numeric, "95 AND OVER" numeric));
Run Code Online (Sandbox Code Playgroud)

表定义:

activities_in_localities_asc

CREATE TABLE adult_social_care.activities_in_localities_asc (
  ogc_fid integer NOT NULL,
  sort numeric(5,0),
  ageband character(12),
  postcode_nospace_ character(8),
  wkb_geometry geometry,
  CONSTRAINT activities_in_localities_asc_pkey PRIMARY KEY (ogc_fid)
);
Run Code Online (Sandbox Code Playgroud)

unit_postcode

CREATE TABLE gazetteers_and_addresses.unit_postcode (
  oogc_fid serial NOT NULL,
  pc_area character(10),
  postcode_nospaces text,
  wkb_geometry geometry
);
Run Code Online (Sandbox Code Playgroud)

如果可能,也可以在最后分配另一个字段,该字段说明所有字段的总和以给出total_count.

如果可以做到这一点,那么我可以使用一个无几何图形的表和unit_postcode.

Erw*_*ter 5

正确的交叉表查询

首先,当您char(n)的表定义中仍然存在时,所提供的查询将不起作用-就像我们在您之前的问题中所讨论的那样。您需要先转换为textvarchar

这适用于您当前的表定义:

SELECT * FROM crosstab(
   'SELECT postcode_nospace_::text, ageband::text, count(ageband) AS ct
    FROM   adult_social_care.activities_in_localities_asc
    GROUP  BY 1, 2
    ORDER  BY 1'

   ,$$VALUES ('18-24'::text), ('25-34'), ('35-44'), ('45-54'), ('55-64')
           , ('65-74'), ('75-84'), ('85-94'), ('95 AND OVER')$$
   )
AS t("postcode" text
   , "18-24" bigint, "25-34" bigint,"35-44" bigint, "45-54" bigint, "55-64" bigint
   , "65-74" bigint, "75-84" bigint, "85-94" bigint, "95 AND OVER" bigint);
Run Code Online (Sandbox Code Playgroud)

我还使用bigint而不是numeric保存另一个不必要的类型转换(count()返回bigint)。

详细解释:

更好的表定义

但最好ALTER是表中的数据类型text像@dezso 建议的那样。或者,如果您想保持长度限制,请添加CHECK约束或使用varchar(n). 永远不要使用char(n).

ALTER TABLE activities_in_localities_asc
   ALTER COLUMN ageband TYPE varchar(12)
 , ALTER COLUMN postcode_nospace_ TYPE varchar(8);

 ALTER TABLE gazetteers_and_addresses.unit_postcode
   ALTER COLUMN pc_area TYPE varchar(10);
Run Code Online (Sandbox Code Playgroud)

然后您不再需要转换原始列值:

SELECT * FROM crosstab(
   'SELECT postcode_nospace_, ageband, count(ageband)
    ...
Run Code Online (Sandbox Code Playgroud)

不过,理想情况下,ageband应该是一个enum或(我的偏好)引用查找表的 ID,而不是简单的、容易出错的text……关键词:规范化

添加总数

建立在您的桌子上,但带有textvarchar()列。

将您的原始查询包装到CTE 中,并在UNION查询中两次调用结果- 第二次调用添加每个邮政编码的总数:

SELECT * FROM crosstab(
  $$WITH cte AS (
      SELECT postcode_nospace_, ageband, count(ageband) AS ct
      FROM   adult_social_care.activities_in_localities_asc  
      GROUP  BY 1, 2
      )
   TABLE  cte  -- original results
   UNION ALL   -- add total per postcode
   SELECT postcode_nospace_, 'total' AS ageband, sum(ct) AS ct
   FROM   cte
   GROUP  BY 1
   ORDER  BY 1$$  -- dollar-quotes to include single quotes easily

   ,$$VALUES ('18-24'::text), ('25-34'), ('35-44'), ('45-54'), ('55-64')
           , ('65-74'), ('75-84'), ('85-94'), ('95 AND OVER'), ('total')$$
   )
AS t("postcode" text
   , "18-24" bigint, "25-34" bigint,"35-44" bigint, "45-54" bigint, "55-64" bigint
   , "65-74" bigint, "75-84" bigint, "85-94" bigint, "95 AND OVER" bigint, "total" bigint);
Run Code Online (Sandbox Code Playgroud)

关于TABLE命令:

相关 CTE 示例: