sql - 交叉表转置查询请求

标签 sql postgresql pivot crosstab

使用 Postgres 9.3.4,我得到了这张表:

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_namecategoryvalue 的数据。您可以通过 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 查询只需要单表扫描并且会更快:

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 参数形式:

添加“汇总”

即每个指标的总数:

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 参数形式)。

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

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

关于sql - 交叉表转置查询请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33072890/

相关文章:

postgresql - 如何在 ubuntu 14.04 中安装 postgis 1.5?

python - Pandas - 将多个分类列旋转到同一组列中

python - 如何在 Spark SQL 中对多列进行透视?

sql - Laravel 查询生成器 : Where pivot not in

sql - Postgres 警告 : pg_query(): Query failed: ERROR: operator does not exist: boolean = integer LINE 1

SQL Server 识别时间戳之间的间隙并分别输出标签

sql - 如何将索引与数据分开存储在Azure上?

sql - 在事务中执行语句 - Sql Server 2005

postgresql - 由于 ECONNREFUSED,Heroku 无法迁移?

SQL日期时间更新时间为00 :00:00 from all other time values