Postgres:使用Case和Group By时所需的聚合函数,但仅在使用join时

时间:2016-10-11 08:39:08

标签: sql postgresql

我的一个postgres查询有一个奇怪的问题。除了一个非常具体的场景之外,该查询在多种场景中可以正常工作,包括各种其他分组,总和等。

下面的代码是计算来自5个不同表的行(忽略这个本身就是哑,这是一个非常糟糕和讨厌的数据库结构的事实)并根据CASE函数中返回的值对它们进行分组。它如下工作正常:

SELECT SUM(count) as count, status FROM
(SELECT count(fsp.id) as count, CASE WHEN fsp.stageonecompletetime = 0 THEN 'in-progress'
WHEN fsp.stagetwocompletetime = 0 THEN 'awaiting-final-review'
WHEN fsp.completedtime = 0 THEN 'awaiting-authorisation'
WHEN fsp.senttodate = 0 THEN 'awaiting-distribution'
WHEN fsp.senttodate > 0 AND superseded = 0 THEN 'completed'
ELSE 'archived' END AS status FROM fleet_documents.fleet_solution_policy fsp WHERE TO_TIMESTAMP(fsp.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(fsp.createdtime) < '2016-10-03' GROUP BY status
UNION ALL
SELECT count(cba.id) as count, CASE WHEN cba.stageonecompletetime = 0 THEN 'in-progress'
WHEN cba.stagetwocompletetime = 0 THEN 'awaiting-final-review'
WHEN cba.completedtime = 0 THEN 'awaiting-authorisation'
WHEN cba.senttodate = 0 THEN 'awaiting-distribution'
WHEN cba.senttodate > 0 AND superseded = 0 THEN 'completed'
ELSE 'archived' END AS status FROM fleet_documents.casing_buyback_agreement cba WHERE TO_TIMESTAMP(cba.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(cba.createdtime) < '2016-10-03' GROUP BY status
UNION ALL
SELECT count(ftp.id) as count, CASE WHEN ftp.stageonecompletetime = 0 THEN 'in-progress'
WHEN ftp.stagetwocompletetime = 0 THEN 'awaiting-final-review'
WHEN ftp.completedtime = 0 THEN 'awaiting-authorisation'
WHEN ftp.senttodate = 0 THEN 'awaiting-distribution'
WHEN ftp.senttodate > 0 AND superseded = 0 THEN 'completed'
ELSE 'archived' END AS status FROM fleet_documents.fleet_tyre_policy ftp WHERE TO_TIMESTAMP(ftp.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(ftp.createdtime) < '2016-10-03' GROUP BY status
UNION ALL
SELECT count(ds.id) as count, CASE WHEN ds.stageonecompletetime = 0 THEN 'in-progress'
WHEN ds.stagetwocompletetime = 0 THEN 'awaiting-final-review'
WHEN ds.completedtime = 0 THEN 'awaiting-authorisation'
WHEN ds.senttodate = 0 THEN 'awaiting-distribution'
WHEN ds.senttodate > 0 AND superseded = 0 THEN 'completed'
ELSE 'archived' END AS status FROM fleet_documents.dealer_sla ds WHERE TO_TIMESTAMP(ds.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(ds.createdtime) < '2016-10-03' GROUP BY status
UNION ALL
SELECT count(cg.id) as count, CASE WHEN cg.stageonecompletetime = 0 THEN 'in-progress'
WHEN cg.stagetwocompletetime = 0 THEN 'awaiting-final-review'
WHEN cg.completedtime = 0 THEN 'awaiting-authorisation'
WHEN cg.senttodate = 0 THEN 'awaiting-distribution'
WHEN cg.senttodate > 0 AND superseded = 0 THEN 'completed'
ELSE 'archived' END AS status FROM fleet_documents.casing_grid cg WHERE TO_TIMESTAMP(cg.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(cg.createdtime) < '2016-10-03' GROUP BY status) allDocuments
GROUP BY status

但是当我必须在查询中加入以允许我按用户过滤结果时,如下所示我得到一个恼人的错误:

SELECT SUM(count) as count, status FROM
    (SELECT count(fsp.id) as count, CASE WHEN fsp.stageonecompletetime = 0 THEN 'in-progress'
        WHEN fsp.stagetwocompletetime = 0 THEN 'awaiting-final-review'
        WHEN fsp.completedtime = 0 THEN 'awaiting-authorisation'
        WHEN fsp.senttodate = 0 THEN 'awaiting-distribution'
        WHEN fsp.senttodate > 0 AND superseded = 0 THEN 'completed'
        ELSE 'archived' END AS status FROM fleet_documents.fleet_solution_policy fsp JOIN users u1 ON u1.id = fsp.user_id WHERE TO_TIMESTAMP(fsp.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(fsp.createdtime) < '2016-10-03' AND lower(u1.username) = 'Test' GROUP BY status
    UNION ALL
    SELECT count(cba.id) as count, CASE WHEN cba.stageonecompletetime = 0 THEN 'in-progress'
        WHEN cba.stagetwocompletetime = 0 THEN 'awaiting-final-review'
        WHEN cba.completedtime = 0 THEN 'awaiting-authorisation'
        WHEN cba.senttodate = 0 THEN 'awaiting-distribution'
        WHEN cba.senttodate > 0 AND superseded = 0 THEN 'completed'
        ELSE 'archived' END AS status FROM fleet_documents.casing_buyback_agreement cba JOIN users u4 ON u4.id = cba.user_id WHERE TO_TIMESTAMP(cba.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(cba.createdtime) < '2016-10-03' AND lower(u4.username) = 'Test' GROUP BY status
    UNION ALL
    SELECT count(ftp.id) as count, CASE WHEN ftp.stageonecompletetime = 0 THEN 'in-progress'
        WHEN ftp.stagetwocompletetime = 0 THEN 'awaiting-final-review'
        WHEN ftp.completedtime = 0 THEN 'awaiting-authorisation'
        WHEN ftp.senttodate = 0 THEN 'awaiting-distribution'
        WHEN ftp.senttodate > 0 AND superseded = 0 THEN 'completed'
        ELSE 'archived' END AS status FROM fleet_documents.fleet_tyre_policy ftp JOIN users u2 ON u2.id = ftp.user_id WHERE TO_TIMESTAMP(ftp.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(ftp.createdtime) < '2016-10-03' AND lower(u2.username) = 'Test' GROUP BY status
    UNION ALL
    SELECT count(ds.id) as count, CASE WHEN ds.stageonecompletetime = 0 THEN 'in-progress'
        WHEN ds.stagetwocompletetime = 0 THEN 'awaiting-final-review'
        WHEN ds.completedtime = 0 THEN 'awaiting-authorisation'
        WHEN ds.senttodate = 0 THEN 'awaiting-distribution'
        WHEN ds.senttodate > 0 AND superseded = 0 THEN 'completed'
        ELSE 'archived' END AS status FROM fleet_documents.dealer_sla ds JOIN users u3 ON u3.id = ds.user_id WHERE TO_TIMESTAMP(ds.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(ds.createdtime) < '2016-10-03' AND lower(u3.username) = 'Test' GROUP BY status
    UNION ALL
    SELECT count(cg.id) as count, CASE WHEN cg.stageonecompletetime = 0 THEN 'in-progress'
        WHEN cg.stagetwocompletetime = 0 THEN 'awaiting-final-review'
        WHEN cg.completedtime = 0 THEN 'awaiting-authorisation'
        WHEN cg.senttodate = 0 THEN 'awaiting-distribution'
        WHEN cg.senttodate > 0 AND superseded = 0 THEN 'completed'
        ELSE 'archived' END AS status FROM fleet_documents.casing_grid cg JOIN users u5 ON u5.id = cg.user_id WHERE TO_TIMESTAMP(cg.createdtime) >= '2016-09-03' AND TO_TIMESTAMP(cg.createdtime) < '2016-10-03' AND lower(u5.username) = 'Test' GROUP BY status) allDocuments
GROUP BY status

我得到的错误如下:错误:列&#34; fsp.stageonecompletetime&#34;必须出现在GROUP BY子句中或用于聚合函数。

现在,我无法理解为什么只有在使用用户表进行连接时才会返回此错误。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

我怀疑你的users表中有一个名为status的列,这改变了组的语义,所以它不再引用别名case表达式。

尝试更改您正在使用的别名。

答案 1 :(得分:1)

这不是真正的答案。 David Aldridge已经给出了正确的答案。

以下是我将如何编写查询。我根本不会加入用户。我会首先将所有表格粘在一起,然后使用它们,就好像它们只是一张桌子一样。我在子查询中对数据进行分类,然后在主查询中使用创建的别名。

select count(*), status
from
(
  select
    case  
      when stageonecompletetime = 0 then 'in-progress'
      when stagetwocompletetime = 0 then 'awaiting-final-review'
      when completedtime = 0 then 'awaiting-authorisation'
      when senttodate = 0 then 'awaiting-distribution'
      when senttodate > 0 and superseded = 0 then 'completed'
      else 'archived' 
    end as status 
  from 
  (
    select user_id, createdtime, stageonecompletetime, stagetwocompletetime, completedtime, senttodate, superseded from fleet_documents.fleet_solution_policy
    union all
    select user_id, createdtime, stageonecompletetime, stagetwocompletetime, completedtime, senttodate, superseded from fleet_documents.casing_buyback_agreement
    union all
    select user_id, createdtime, stageonecompletetime, stagetwocompletetime, completedtime, senttodate, superseded from fleet_documents.fleet_tyre_policy
    union all
    select user_id, createdtime, stageonecompletetime, stagetwocompletetime, completedtime, senttodate, superseded from fleet_documents.dealer_sla
    union all
    select user_id, createdtime, stageonecompletetime, stagetwocompletetime, completedtime, senttodate, superseded from fleet_documents.casing_grid
  ) data
) classified
where createdtime >= '2016-09-03' 
  and createdtime < '2016-10-03'
  and user_id in (select id from users where lower(username) = 'test')
group by status
order by count(*);

只有当速度太慢时才会将WHERE子句移动到单表查询中。因为查询更具可读性和可维护性。