您需要动态生成crosstab() 调用。
但由于 SQL 不允许动态返回类型,您需要一个两步工作流程:
- 生成查询
- 执行查询
如果您不熟悉crosstab(),请先阅读以下内容:
从creation_date 生成月份很奇怪,而不是年份。为简化起见,我改用组合列 year_month。
查询以生成crosstab() 查询:
SELECT format(
$f$SELECT * FROM crosstab(
$q$
SELECT to_char(date_trunc('month', creation_date), 'YYYY_Month') AS year_month
, marking
, COUNT(*) AS ct
FROM invoices
GROUP BY date_trunc('month', creation_date), marking
ORDER BY date_trunc('month', creation_date) -- optional
$q$
, $c$VALUES (%s)$c$
) AS ct(year_month text, %s);
$f$, string_agg(quote_literal(sub.marking), '), (')
, string_agg(quote_ident (sub.marking), ' int, ') || ' int'
)
FROM (SELECT DISTINCT marking FROM invoices ORDER BY 1) sub;
如果表invoices 是大,只有很少 个不同的值用于标记(这似乎很可能),那么有更快的方法来获得不同的值。见:
生成表单的查询:
SELECT * FROM crosstab(
$q$
SELECT to_char(date_trunc('month', creation_date), 'YYYY_Month') AS year_month
, marking
, COUNT(*) AS ct
FROM invoices
GROUP BY date_trunc('month', creation_date), marking
ORDER BY date_trunc('month', creation_date) -- optional
$q$
, $c$VALUES ('Delivered'), ('Not Delivered'), ('Not Received')$c$
) AS ct(year_month text, "Delivered" int, "Not Delivered" int, "Not Received" int);
简化查询不需要“额外的列。见:
注意date_trunc('month', creation_date) 在GROUP BY 和ORDER BY 中的使用。这会产生一个有效的排序顺序,而且速度也更快。见:
还要注意使用美元引号以避免引用地狱。见:
没有条目的月份不会显示在结果中,并且现有月份的任何标记都不会显示为NULL。如果需要,您可以进行调整。见:
然后执行生成的查询。
dbfiddle here(重用
爱德华的小提琴,赞!)
见:
在 psql 中
在psql 中,您可以使用\qexec 立即执行生成的查询。见:
在 Postgres 9.6 或更高版本中,您还可以使用\crosstabview 代替 crosstab():
test=> SELECT to_char(date_trunc('month', creation_date), 'YYYY_Month') AS year_month
test-> , marking
test-> , COUNT(*) AS count
test-> FROM invoices
test-> GROUP BY date_trunc('month', creation_date), 2
test-> ORDER BY date_trunc('month', creation_date)\crosstabview
year_month | Not Received | Delivered | Not Delivered
----------------+--------------+-----------+---------------
2020_January | 1 | 1 | 1
2020_March | | 2 | 2
2021_January | 1 | 1 | 2
2021_February | 1 | |
2021_March | | 1 |
2021_August | 2 | 1 | 1
2022_August | | 2 |
2022_November | 1 | 2 | 3
2022_December | 2 | |
(9 rows)
请注意,\crosstabview - 与 crosstab() 不同 - 不支持“额外”列。如果你坚持年份和月份分开,你需要crosstab()。
见: