Postgresql "Column must appear in the GROUP BY clause or be used in an aggregate function" 在 ORDER BY 子句中使用 CASE 表达式时

Postgresql "Column must appear in the GROUP BY clause or be used in an aggregate function" when using CASE expression inside ORDER BY clause

我收到此查询的 column "measurementResults.value" must appear in the GROUP BY clause or be used in an aggregate function 错误:

SELECT avg("measurementResults"."value") AS "value",
       min("measurement"."timestamp") AS "timestamp",
       min("testProtocolItemResults"."ranking") AS "ranking"
FROM "measurement"
JOIN "measurementResults" ON "measurementResults"."measurement" = "measurement"."id"
JOIN "conditions" ON "conditions"."measurement" = "measurement"."id"
JOIN "testProtocolItemResults" ON "testProtocolItemResults"."id" = "measurementResults"."testProtocolItemResults"
JOIN "testProtocolSessionItem" ON "testProtocolSessionItem"."id" = "measurement"."testProtocolSessionItem"
WHERE "measurement"."athlete" = 334
  AND "measurementResults"."testProtocolItemResults" = 1
  AND "conditions"."conditions" = '6'
GROUP BY "testProtocolSessionItem"."testProtocolSession",
         "testProtocolItemResults"."ranking"
ORDER BY (CASE
              WHEN "ranking" = 'greater'::text THEN "value"
              ELSE NULL::double precision
          END) DESC
LIMIT 3

如果我像这样更改 ORDER BY 子句,它会按预期工作:

...
ORDER BY "value" DESC
...

我做错了什么?

您的问题有几个根源:

最重要的是,不要对与输入列(同名)不同的输出列使用同名。这是一个加载 foot-gun.

其次,养成在涉及多个表的复杂查询中使用table-qualify 所有列的习惯。即使这看起来可行,它也可能已经在做一些超出您想象的事情。即使它工作正常,如果更改了任何列名(添加、删除、重命名),它也可能会在以后中断。运气不好,它会悄无声息地中断,而您的查询会很愉快地产生废话。

第三,SQL 标准,它的可见性规则有些混乱。参见:

  • GROUP BY + CASE statement

在您的替代查询中,"value" 解析为 output"value",它隐藏了 [=14] 中任何同名的输入列=].这按预期工作(也就是说,如果您实际上打算以输出列为目标)。

在失败的查询中,"value" 解析为 input"measurementResults.value"。您不能将输出列放入 ORDER BY 中的新计算中,您只能“按原样”使用它们。因此,在不影响输出列的情况下,"value" 解析为输入列(现在不再隐藏)。这会导致报告的错误。显然,您不能在聚合后按输入列排序 - 除非您直接或间接按它分组。

您可以通过以下方式修复您的查询:

ORDER  BY (ranking = 'greater') IS TRUE, "value" DESC

对所有 ranking = 'greater' 不正确的行进行排序 - 就像您的 CASE 表达式一样。所以对待 nullfalse 一样。

细微差别:前导行按 value 排序,而您的原始行会以任意顺序列出它们。可能受欢迎也可能不受欢迎。

  • Sorting null values after all others, except special
  • Best way to check for "empty or null value"

我假设您知道 null 值按降序排列在顶部?你可以改变它吗?参见:

  • Sort by column ASC, but NULL values first?

如果这还不够好(或对于更复杂的表达式),您必须更加详细和明确:一种方法是将整个查询包装到一个子查询中,并在外部 SELECT:

SELECT avg_value, min_timestamp, min_ranking
FROM  (
   SELECT ir.ranking                          -- !
        , avg(mr."value")    AS avg_value     -- !
        , min(m."timestamp") AS min_timestamp -- !
        , min(ir.ranking)    AS min_ranking   -- !
   FROM   measurement               m
   JOIN   "measurementResults"      mr ON mr.measurement = m.id
   JOIN   conditions                c  ON c.measurement = m.id
   JOIN   "testProtocolItemResults" ir ON ir.id = mr."testProtocolItemResults"
   JOIN   "testProtocolSessionItem" si ON si.id = m."testProtocolSessionItem"
   WHERE  m."athlete" = 334
   AND    mr."testProtocolItemResults" = 1
   AND    c."conditions" = '6'
   GROUP  BY si."testProtocolSession", ir.ranking
   ) sub
ORDER  BY CASE WHEN ranking = 'greater' THEN "value" END DESC
LIMIT  3

特别是对于 LIMIT 小的查询,如果 Postgres 不能再优化查询计划,这可能会更昂贵。

旁白:
使用 legal, loser-case identifiers,这样您就不必 double-quote.
并使用 table aliases 来 de-noise 你的大查询。