带有 CASE 和聚合的 PostgreSQL crosstab() 替代方案

PostgreSQL crosstab() alternative with CASE and aggregates

我想创建一个数据透视表 table 视图,显示每个 travel_mode.

的月度预订总和

Table bookings:

  timestamp
, bookings
, provider_id

Table providers:

  provider_id
, travel_mode

Pivot table 函数和交叉表函数不用于执行此操作。所以我正在尝试使用 JOIN 和 CASE。以下是查询:

  SELECT b.month, 
  (CASE WHEN p.travel_mode=train then b.amount end)train,
  (CASE WHEN p.travel_mode=bus then b.amount end)bus, 
  (CASE WHEN p.travel_mode=air then b.amount end)air
  FROM 
  (SELECT  to_char(date_,month) as month, travel_mode, sum(bookings) as amount
  from bookings as b
  join providers as p
  on b.provider_id=p.provider_id
  group by b.month, p.travel_mode)
  group by b.month;

但是我收到一条错误消息:

subquery in FROM must have an alias LINE 6:

当我添加别名时,它会抛出一条错误消息:

column p.travel_mode must appear in the GROUP BY clause or be used in an aggregate function 
LINE 2:

最后的结果应该是这样的

Month       Air             Bus            Train  
01          Amount(air)     Amount(Bus)    Amount(train)

我感觉这是某个地方的小错误,但我根本无法弄清楚。

P.S。我不得不删除问题中的所有引号,因为它不允许我 post 这个。但是在实际查询中会处理这些问题。

您必须按照错误消息所述命名子查询:

SELECT b.month, 
(CASE WHEN p.travel_mode=train then b.amount end)train,
(CASE WHEN p.travel_mode=bus then b.amount end)bus, 
(CASE WHEN p.travel_mode=air then b.amount end)air
FROM 
(SELECT  to_char(date_,month) as month, travel_mode, sum(bookings) as amount
from bookings as b
join providers as p
on b.provider_id=p.provider_id
group by b.month, p.travel_mode)
**as foo** group by b.month;

移除星号使其生效。

多个问题。缺少的 table 别名只是其中之一。此查询应该有效:

SELECT month
     , sum(CASE WHEN travel_mode = 'train' THEN amount END) AS train
     , sum(CASE WHEN travel_mode = 'bus'   THEN amount END) AS bus
     , sum(CASE WHEN travel_mode = 'air'   THEN amount END) AS air
FROM  (
   SELECT to_char(timestamp, 'MM') AS month, travel_mode, sum(bookings) AS amount
   FROM   bookings  b
   JOIN   providers p USING (provider_id)
   GROUP  BY month, p.travel_mode
   ) sub
GROUP  BY month;
  • 字符串文字缺少单引号。 (您似乎已经删除了那些在错误印象下您无法 post 引用的内容。)

  • 缺少子查询的 table 别名 - 正如第一条错误消息所说。

  • 在外部查询中,子查询中基础 table 的 table 名称(或别名)不可见。只有子查询的 table 别名是。因为只有 一个 子查询,所以根本不需要 table 限定。

  • month 是输出列名称(不在基础 table 中),因此 table 限定条件 b.month 也是错误的。

  • 您似乎想要几个月的两位数。使用 template pattern 'MM' 而不是 'month' 和 to_char().

  • 外部查询中的聚合无法正常工作 - 就像您的第二条错误消息所说的那样。您必须将外部 CASE 表达式包装在聚合函数中。在这种情况下,您还不如使用 min()max(),因为在子查询之后永远不会超过一行。

  • 仍然不清楚 date_ 的来源?你是说 timestamp? (这不是一个好的标识符)。

但是你不需要子查询开始,可以简化为:

SELECT to_char(timestamp, 'MM') AS month
     , sum(CASE WHEN p.travel_mode = 'train' THEN b.bookings END) AS train
     , sum(CASE WHEN p.travel_mode = 'bus'   THEN b.bookings END) AS bus
     , sum(CASE WHEN p.travel_mode = 'air'   THEN b.bookings END) AS air
FROM   bookings  b
JOIN   providers p USING (provider_id)
GROUP  BY 1;

为了获得最佳性能,您仍应使用 crosstab(),但:

  • PostgreSQL Crosstab Query