交叉表转置查询请求

Crosstab transpose query request

使用 Postgres 9.3.4,我得到了这个 table:

create table tbl1(country_code text, metric1 int, metric2 int, metric3 int);
insert into tbl1 values('us', 10, 20, 30);
insert into tbl1 values('uk', 11, 21, 31);
insert into tbl1 values('fr', 12, 22, 32);

我需要一个交叉表查询来将其转换为:

create table tbl1(metric text, us int, uk int, fr int);
insert into tbl1 values('metric1', 10, 11, 12);
insert into tbl1 values('metric2', 20, 21, 22);
insert into tbl1 values('metric3', 30, 31, 32);

作为额外的奖励,我想要汇总:

create table tbl1(metric text, total int, us int, uk int, fr int);
insert into tbl1 values('metric1', 33, 10, 11, 12);
insert into tbl1 values('metric2', 63, 20, 21, 22);
insert into tbl1 values('metric3', 93, 30, 31, 32);

我已经看完了交叉表规范,我已经用 case 语句编写了它,但是它非常不守规矩而且很长,所以精通交叉表的人可以快速查询一下,这样我就可以继续了吗?

特别困难的是您的数据还没有准备好进行交叉制表。您需要 row_name类别 形式的数据。您可以通过 UNION 查询获得:

SELECT 'metric1' AS metric, country_code, metric1 FROM tbl1
UNION ALL
SELECT 'metric2' AS metric, country_code, metric2 FROM tbl1
UNION ALL
SELECT 'metric3' AS metric, country_code, metric3 FROM tbl1
ORDER  BY 1, 2 DESC;

但是智能 LATERAL 查询只需要一次 table 扫描并且会更快:

SELECT x.metric, t.country_code, x.val
FROM   tbl1 t
     , LATERAL (VALUES
         ('metric1', metric1)
       , ('metric2', metric2)
       , ('metric3', metric3)
       ) x(metric, val)
ORDER  BY 1, 2 DESC;

相关:

使用带有 1 个参数的 crosstab() 的简单形式,并将此查询作为输入:

SELECT * FROM crosstab(
   $$
   SELECT x.metric, t.country_code, x.val
   FROM   tbl1 t
   , LATERAL (
      VALUES
        ('metric1', metric1)
      , ('metric2', metric2)
      , ('metric3', metric3)
      ) x(metric, val)
   ORDER  BY 1, 2 DESC
   $$
   ) AS ct (metric text, us int, uk int, fr int);

按字母降序列出国家名称(就像在您的演示中一样)。 这还假设所有指标都已定义 NOT NULL.

如果其中之一或两者都不是,请改用 2 参数形式:

  • PostgreSQL Crosstab Query

添加“汇总”

即每个指标的总数:

SELECT * FROM crosstab(
   $$
   SELECT x.metric, t.country_code, x.val
   FROM  (
      TABLE tbl1
      UNION ALL
      SELECT 'zzz_total', sum(metric1)::int, sum(metric2)::int, sum(metric3)::int  -- etc.
      FROM tbl1
      ) t
   , LATERAL (
      VALUES
        ('metric1', metric1)
      , ('metric2', metric2)
      , ('metric3', metric3)
      ) x(metric, val)
   ORDER  BY 1, 2 DESC
   $$
   ) AS ct (metric text, total int, us int, uk int, fr int);

'zzz_total' 是任意标签,必须按字母顺序最后排序(或者您需要 crosstab() 的 2 参数形式)。

如果您有 很多 个指标列,您可能需要动态构建查询字符串。相关:

  • How to perform the same aggregation on every column, without listing the columns?

另请注意,即将推出的 Postgres 9.5(目前为测试版)引入了专用的 SQL clause for ROLLUP
相关: