PostgreSQL列必须通过子句出现在组中,或在汇总函数中使用。当在子句中使用案例表达式在订单中

发布于 2025-01-23 07:10:25 字数 1149 浏览 0 评论 0原文

我获得列“ MeadurementResults.value”必须通过子句出现在组中,或在汇总函数中使用此查询的错误错误:

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 "value" DESC
...

我在做什么错?

I get the column "measurementResults.value" must appear in the GROUP BY clause or be used in an aggregate function error for this query:

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

If I change the ORDER BY clause like this, it works as expected:

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

What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

春花秋月 2025-01-30 07:10:25

您的问题有几个根源:

最重要的是,不要将相同名称用于与输入列不同(同名)不同的输出列。那是一个装满的脚枪。

其次,将其养成表格质量的习惯列中,用于复杂查询中的涉及多个表。即使这似乎有效,它也可能已经在做其他事情。即使它正常工作,如果更改了任何列名(添加,删除),它也可能会破裂。运气不好,它默默地破裂了,您的疑问是胡说八道后的疑问。

第三,SQL标准有一些令人困惑的可见性规则。 :

请参阅 “ value” 解决输出“ value”,它隐藏了 订单中同名的任何输入列。这是按预期工作的(也就是说,如果您实际上是要针对输出列)。

在您的失败查询中,“ value”解决到输入“ MeadurementResults.Value”。您不能将输出列通过 以顺序进行新的计算,您只能使用“按原样”使用它们。因此,随着输出列的范围,“ value”可以解决到输入列(现在不再隐藏)。这导致报告的错误。显然,汇总后您不能通过输入列订购 - 除非您直接或间接地通过它进行分组。

您可以通过:

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

所有行排名='更大'不正确的所有行 - 就像您的case表达式一样。因此,处理nullfalse都一样。

微妙的差异:那些领先的行被value排序,而您的原始行将按任意顺序列出它们。可能会或可能不欢迎。

我假设您知道null值以降序排序排序吗?你可以改变吗?请参阅:

如果这还不够好(或为更复杂的表达式),您必须更详细且明确:一种方法是将整个查询包装到一个子查询中,然后在Outer 中订购(和限制!)选择

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标识符,因此您不必双引号。

并使用

Your problem has a couple of roots:

Most importantly, don't use the same name for an output column that is distinct from an input column (of the same name). That's a loaded foot-gun.

Secondly, make it a habit to table-qualify all columns used in a complex query involving multiple tables. Even if that seems to work, it might already be doing something else than you think. And even if it works correctly it may break later, if any column names are changed (added, removed renamed). With some bad luck it breaks silently, and your query happily ever after produces nonsense.

Thirdly, the SQL standard, which has somewhat confusing visibility rules. See:

In your working alternative query, "value" resolves to the output column "value", which hides any input column of the same name in ORDER BY. That works as expected (that is, if you actually meant to target the output column).

In your failing query, "value" resolves to the input column "measurementResults.value". You cannot throw output columns into a new computation in ORDER BY, you can only use them "as is". So, with output columns out of the way, "value" resolves to the input column (now not hidden any more). And that leads to the reported error. Obviously, you cannot order by an input column after aggregating - except if you grouped by it, directly or indirectly.

You could repair your query with:

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

The sorts all rows where ranking = 'greater' is not true to the top - like your CASE expression would. So treating null and false alike.

Subtle difference: Those leading rows are sorted by value, while your original would list them in arbitrary order. May or may not be welcome.

I assume you are aware that null values sort on top in descending order? And that you can change that? See:

If that's not good enough (or for more complex expressions), you must be more verbose and explicit: one way is to wrap the whole query into a subquery, and order (and limit!) in the outer 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

Especially for queries with a small LIMIT, this may be more expensive if Postgres cannot optimize the query plan as well any more.

Aside:
Use legal, loser-case identifiers, so you don't have to double-quote.
And use table aliases to de-noise your big queries.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文