小编典典

PostgreSQL交叉表查询

sql

有谁知道如何在PostgreSQL中创建交叉表查询吗?
例如,我有下表:

Section    Status    Count
A          Active    1
A          Inactive  2
B          Active    4
B          Inactive  5

我希望查询返回以下交叉表:

Section    Active    Inactive
A          1         2
B          4         5

这可能吗?


阅读 388

收藏
2021-04-10

共1个答案

小编典典

每个数据库都安装一次附加模块tablefunc ,该模块提供的功能。从Postgres 9.1开始,您可以使用:crosstab()CREATE EXTENSION

CREATE EXTENSION IF NOT EXISTS tablefunc;

改进的测试用例


CREATE TABLE tbl (
   section   text
 , status    text
 , ct        integer  -- "count" is a reserved word in standard SQL
);

INSERT INTO tbl VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                    , ('C', 'Inactive', 7);  -- ('C', 'Active') is missing

简单形式-不适合缺少属性
crosstab(text)具有1个输入参数:

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- needs to be "ORDER BY 1,2" here
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

返回值:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!

无需强制转换和重命名。
请注意以下错误结果C:7在第一列中填写了该值。有时,此行为是理想的,但对于此用例而言并非如此。
在简单的形式也被限制到恰好在所提供的输入查询三列:ROW_NAME,类别,值。像下面的2参数替代中一样,没有多余的空间容纳额外的列。

Safe form

crosstab(text, text)具有2个输入参数:

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- could also just be "ORDER BY 1" here

  , $$VALUES ('Active'::text), ('Inactive')$$
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

返回值:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |        |        7  -- !!

请注意的正确结果C。

所述第二参数可以是任何查询,返回一个行每属性匹配在端列定义的顺序。通常,您将需要从基础表中查询不同的属性,如下所示:

'SELECT DISTINCT attribute FROM tbl ORDER BY 1'

在手册中。

由于您仍然必须拼写出列定义列表中的所有列(预定义的变体除外),因此,在经过演示的表达式中提供简短列表通常更为有效:crosstabN()VALUES

$$VALUES ('Active'::text), ('Inactive')$$)

或(不在手册中):

$$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists

我使用美元报价来简化报价。

您甚至可以使用-输出具有不同数据类型的列,crosstab(text, text)只要值列的文本表示形式是目标类型的有效输入即可。这样,你可能有不同的属性种类和产量text,date,numeric等,为各自的属性。本手册本章crosstab(text, text)结尾处有一个代码示例。

\crosstabview 在psql中
Postgres 9.6将此元命令添加到其默认的交互式终端psql中。您可以运行将用作第一个crosstab()参数的查询,并将其输入\crosstabview(立即或在下一步中)。喜欢:

db=> SELECT section, status, ct FROM tbl \crosstabview
与上面类似的结果,但是它是客户端专用的表示功能。输入行的处理略有不同,因此ORDER BY不是必需的。\crosstabview手册中的详细信息。该页面底部还有更多代码示例。

在以前接受的答案是过时的。

该功能的变型crosstab(text, integer)已过时。第二个integer参数将被忽略。我引用了当前的手册:

crosstab(text sql, int N) …
过时版本crosstab(text)。N现在将忽略该参数,因为值列的数量始终由调用查询确定

不必要的转换和重命名。

如果某行没有所有属性,它将失败。请参阅上面具有两个输入参数的安全变量,以正确处理缺少的属性。

ORDER BY是的一参数形式crosstab()。手册:

在实践中,SQL查询应始终指定ORDER BY 1,2以确保输入行正确排序

2021-04-10