sch*_*one 180 sql postgresql pivot case crosstab
有没有人知道如何在PostgreSQL中创建交叉表查询?
例如,我有下表:
Section Status Count
A Active 1
A Inactive 2
B Active 4
B Inactive 5
Run Code Online (Sandbox Code Playgroud)
我想查询返回以下交叉表:
Section Active Inactive
A 1 2
B 4 5
Run Code Online (Sandbox Code Playgroud)
这可能吗?
Erw*_*ter 299
每个数据库安装一次附加模块tablefunc
,提供该功能.自Postgres 9.1以来,您可以使用:crosstab()
CREATE EXTENSION
CREATE EXTENSION IF NOT EXISTS tablefunc;
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
返回:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | 7 | -- !!
C
:7
为第一列填写值.有时,这种行为是可取的,但不适用于此用例.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);
Run Code Online (Sandbox Code Playgroud)
返回:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | | 7 -- !!
请注意正确的结果C
.
所述第二参数可以是返回一个任意查询行每属性匹配在端列定义的顺序.通常,您需要查询基础表中的不同属性,如下所示:
'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
Run Code Online (Sandbox Code Playgroud)
这是在手册中.
由于您必须拼写列定义列表中的所有列(预定义变体除外),因此在表达式中提供简短列表通常更为有效:crosstabN()
VALUES
$$VALUES ('Active'::text), ('Inactive')$$)
Run Code Online (Sandbox Code Playgroud)
或(不在手册中):
$$SELECT unnest('{Active,Inactive}'::text[])$$ -- short syntax for long lists
Run Code Online (Sandbox Code Playgroud)我使用美元报价使报价更容易.
你甚至可以输出列有不同的数据类型有crosstab(text, text)
-只要值列文表示是目标类型的有效输入.这样,你可能有不同的种类和输出的属性text
,date
,numeric
对各自的属性等.本手册章节crosstab(text, text)
末尾有一个代码示例.
db <> 在这里小提琴
使用Tablefunc在多列上进行旋转 - 还演示了提到的"额外列"
\crosstabview
在psql中Postgres 9.6将此元命令添加到其默认的交互式终端psql中.您可以运行您将用作第一个crosstab()
参数的查询并将其提供给\crosstabview
(立即或在下一步中).喜欢:
db=> SELECT section, status, ct FROM tbl \crosstabview
Run Code Online (Sandbox Code Playgroud)
与上面类似的结果,但它只是客户端的表示功能.输入行的处理方式略有不同,因此ORDER BY
不是必需的.\crosstabview
手册中的详细信息.该页面底部有更多代码示例.
关于dba.SE的相关答案,DanielVérité(psql特性的作者):
在以前接受的答案是过时的.
该功能的变体crosstab(text, integer)
已过时.第二个integer
参数被忽略.我引用当前的手册:
crosstab(text sql, int N)
...已过时的版本
crosstab(text)
.N
现在忽略该参数,因为值列的数量始终由调用查询确定
不必要的铸造和重命名.
如果某行没有所有属性,则会失败.请参阅上面两个输入参数的安全变体,以正确处理缺少的属性.
ORDER BY
在单参数形式中是必需的crosstab()
.手册:
实际上,SQL查询应始终指定
ORDER BY 1,2
以确保输入行的顺序正确
Jer*_*hka 29
您可以使用附加模块tablefunc的crosstab()
功能- 您必须为每个数据库安装一次.从PostgreSQL 9.1开始,你可以使用它:CREATE EXTENSION
CREATE EXTENSION tablefunc;
Run Code Online (Sandbox Code Playgroud)
在你的情况下,我相信它看起来像这样:
CREATE TABLE t (Section CHAR(1), Status VARCHAR(10), Count integer);
INSERT INTO t VALUES ('A', 'Active', 1);
INSERT INTO t VALUES ('A', 'Inactive', 2);
INSERT INTO t VALUES ('B', 'Active', 4);
INSERT INTO t VALUES ('B', 'Inactive', 5);
SELECT row_name AS Section,
category_1::integer AS Active,
category_2::integer AS Inactive
FROM crosstab('select section::text, status, count::text from t',2)
AS ct (row_name text, category_1 text, category_2 text);
Run Code Online (Sandbox Code Playgroud)
ara*_*nid 24
SELECT section,
SUM(CASE status WHEN 'Active' THEN count ELSE 0 END) AS active, --here you pivot each status value as a separate column explicitly
SUM(CASE status WHEN 'Inactive' THEN count ELSE 0 END) AS inactive --here you pivot each status value as a separate column explicitly
FROM t
GROUP BY section
Run Code Online (Sandbox Code Playgroud)
JSON聚合解决方案:
CREATE TEMP TABLE t (
section text
, status text
, ct integer -- don't use "count" as column name.
);
INSERT INTO t VALUES
('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
, ('C', 'Inactive', 7);
SELECT section,
(obj ->> 'Active')::int AS active,
(obj ->> 'Inactive')::int AS inactive
FROM (SELECT section, json_object_agg(status,ct) AS obj
FROM t
GROUP BY section
)X
Run Code Online (Sandbox Code Playgroud)