spa*_*kle 2 postgresql pivot-table dynamic-sql crosstab postgres-crosstab
我必须显示这样的表格:
| 年 | 月 | 发表 | 没送到 | 没收到 |
|---|---|---|---|---|
| 2021年 | 扬 | 10 | 86 | 75 |
| 2021年 | 二月 | 13 | 36 | 96 |
| 2021年 | 行进 | 49 | 7 | 61 |
| 2021年 | 四月 | 3 | 21 | 72 |
使用此查询生成的原始数据:
SELECT
year,
TO_CHAR( creation_date, 'Month') AS month,
marking,
COUNT(*) AS count
FROM invoices
GROUP BY 1,2,3
Run Code Online (Sandbox Code Playgroud)
我尝试过使用crosstab()但出现错误:
SELECT * FROM crosstab('
SELECT
year,
TO_CHAR( creation_date, ''Month'') AS month,
marking,
COUNT(*) AS count
FROM invoices
GROUP BY 1,2,3
') AS ct(year text, month text, marking text)
Run Code Online (Sandbox Code Playgroud)
我不想手动输入所有标记值,因为它们很多。
Run Code Online (Sandbox Code Playgroud)ERROR: invalid source data SQL statement DETAIL: The provided SQL must return 3 columns: rowid, category, and values.
1. 具有有限marking值列表的静态解决方案:
SELECT year
, TO_CHAR( creation_date, 'Month') AS month
, COUNT(*) FILTER (WHERE marking = 'Delivered') AS Delivered
, COUNT(*) FILTER (WHERE marking = 'Not delivered') AS "Not delivered"
, COUNT(*) FILTER (WHERE marking = 'Not Received') AS "Not Received"
FROM invoices
GROUP BY 1,2
Run Code Online (Sandbox Code Playgroud)
2. 具有大量marking值的完整动态解决方案:
该提案是A和Bcrosstab中提出的解决方案的替代解决方案。
这里提出的解决方案只需要一个composite type可以动态创建的专用解决方案,然后它依赖于jsonb类型和标准函数:
从您的查询开始,该查询计算每年、每月和marking值的行数:
jsonb_object_agg函数,结果行首先按年和月聚合到与值
对应的jsonb对象中,并且其jsonb keysmarkingjsonb values
与计数相对应的jsonb对象转换为记录。jsonb_populate_record首先,我们动态创建一个composite type对应于有序值列表的marking值:
CREATE OR REPLACE PROCEDURE create_composite_type() LANGUAGE plpgsql AS $$
DECLARE
column_list text ;
BEGIN
SELECT string_agg(DISTINCT quote_ident(marking) || ' bigint', ',' ORDER BY quote_ident(marking) || ' bigint' ASC)
INTO column_list
FROM invoices ;
EXECUTE 'DROP TYPE IF EXISTS composite_type' ;
EXECUTE 'CREATE TYPE composite_type AS (' || column_list || ')' ;
END ;
$$ ;
CALL create_composite_type() ;
Run Code Online (Sandbox Code Playgroud)
然后以下查询提供了预期结果:
SELECT a.year
, TO_CHAR(a.year_month, 'Month') AS month
, (jsonb_populate_record( null :: composite_type
, jsonb_object_agg(a.marking, a.count)
)
).*
FROM
( SELECT year
, date_trunc('month', creation_date) AS year_month
, marking
, count(*) AS count
FROM invoices AS v
GROUP BY 1,2,3
) AS a
GROUP BY 1,2
ORDER BY month
Run Code Online (Sandbox Code Playgroud)
显然,如果值列表可能随时间变化,那么您必须在执行查询之前marking调用该过程。create_composite_type()如果您不更新composite_type,查询仍然可以工作(没有错误!),但一些旧的标记值可能已过时(不再使用),并且查询结果中可能会丢失一些新的标记值(不显示为列) 。
请参阅dbfiddle中的完整演示。
| 归档时间: |
|
| 查看次数: |
6937 次 |
| 最近记录: |