PG::Error: SELECT DISTINCT, ORDER BY 表达式必须出现在选择列表中

Posted

技术标签:

【中文标题】PG::Error: SELECT DISTINCT, ORDER BY 表达式必须出现在选择列表中【英文标题】:PG::Error: SELECT DISTINCT, ORDER BY expressions must appear in select list 【发布时间】:2012-09-23 11:19:22 【问题描述】:

ActionView::Template::Error (PG::Error: ERROR: for SELECT DISTINCT, ORDER BY 表达式必须出现在选择列表中

我正在创建一个活动网站,并尝试按活动的开始时间对呈现的 rsvps 进行排序。有很多 RSVPS,所以我将它们与不同的分组,但在过去的几天里,我在对结果进行排序时遇到了很多困难,而没有在 PG 上弹出这个错误。我已经查看了有关该主题的一些先前问题,但仍然很迷茫。我怎样才能让它工作?非常感谢!

@rsvps = Rsvp.where(:voter_id => current_user.following.collect |f| f["id"], :status => 'going').where("start_time > ? AND start_time < ?", Time.now, Time.now + 1.month).order("count_all desc").count(:group => :event_id).collect  |f| f[0] 

<%= render :partial => 'rsvps/rsvp', :collection => Rsvp.where(:event_id => @rsvps).select("DISTINCT(event_id)").order('start_time asc') %>

【问题讨论】:

【参考方案1】:

我知道这是一个相当老的问题,但我只是在脑海中经历了一个小例子,它帮助我理解了为什么 Postgres 对 SELECT DISTINCT / ORDER BY 列有这种看似奇怪的限制。

假设您的 Rsvp 表中有以下数据:

 event_id |        start_time
----------+------------------------
    0     | Mar 17, 2013  12:00:00
    1     |  Jan 1, 1970  00:00:00
    1     | Aug 21, 2013  16:30:00
    2     |  Jun 9, 2012  08:45:00

现在您要获取不同 event_id 的列表,按它们各自的 start_time 排序。但是1 应该去哪里呢?它应该先出现,因为一个元组从 1970 年 1 月 1 日开始,还是应该因为 2013 年 8 月 21 日而最后出现?

由于数据库系统无法为您做出决定,并且查询的语法不能依赖于它可能正在操作的实际数据(假设 event_id 是唯一的),因此我们只能通过以下方式进行排序SELECT 子句中的列。

至于实际问题 - Matthew 的答案的替代方法是使用聚合函数,如 MINMAX 进行排序:

  SELECT event_id
    FROM Rsvp
GROUP BY event_id
ORDER BY MIN(start_time)

start_time 上的显式分组和聚合允许数据库提出结果元组的明确排序。但是请注意,在这种情况下,可读性绝对是一个问题;)

【讨论】:

此外,按 MIN 或 MAX 排序不起作用。它只是给出了同样的错误。 @Jagger,您可能指的是旧版本的 mysql - 因为版本 5.7.5 MySQL 默认启用 ONLY_FULL_GROUP_BY,这不允许这种确切情况并产生错误消息 (Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'test.rsvp.start_time' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by) 此更改记录在MySQL 5.7 changelog @AdrianoKF 奇怪,因为我使用的是mysql-5.7.17-winx64 奇怪,我只是试图重现您的行为并收到上述错误消息 - 您是否可能在 event_id 列上添加了 PK/唯一约束?如果您愿意,我们可以在聊天中继续讨论! @AdrianoKF 我刚刚使用最新版本的 MySQL mysql-8.0.2-dmr-winx64(开发版本)进行了测试。现在在 MySQL 中确实存在一个错误,就像在 PostgreSQL 中一样。无论如何,您使用 MIN 和 MAX 作为解决方法的查询在 PostgreSQL 中也不起作用。【参考方案2】:

ORDER BY 子句只能在应用 DISTINCT 之后应用。由于 DISTINCT 操作仅考虑 SELECT 语句中的字段,因此 ORDER BY 中仅可使用这些字段。

从逻辑上讲,如果您只想要一个不同的 event_id 值列表,那么它们出现的顺序应该是无关紧要的。如果顺序确实很重要,那么您应该将 start_time 添加到 SELECT 列表中,以便有顺序的上下文。

另外,这两个 SELECT 子句是不等价的,所以要小心:

SELECT DISTINCT(event_id, start_time) FROM ...

SELECT DISTINCT event_id, start_time FROM ...

第二个是你想要的形式。第一个将返回一系列记录,其中数据表示为 ROW 构造(内部带有元组的单列)。第二个将返回正常的数据输出列。它仅在 ROW 构造被缩减的单列情况下按预期工作,因为它只是单列。

【讨论】:

哇,你太棒了!很好的解释,你刚刚为我的生活增加了几天:D “从逻辑上讲,如果您只想要一个不同的 event_id 值列表,它们出现的顺序应该是无关紧要的。” - 我见过一个应用程序,其中有 order by 和 limit,所以假设只选择 10 个第一个和不同的项目(在这种情况下为事件)。 我在 Hibernate 中遇到了同样的问题。原因是我写了... ORDER BY time 而不是... ORDER BY obj.time "ORDER BY 子句只能在应用了 DISTINCT 之后应用。"。有趣的是,这对 MySQL 来说没有任何问题。 嗨@Matthew Wood,我在这里也有类似的问题,但与 jsonb 订购有关:***.com/questions/54305695/…【参考方案3】:

因为您使用的是 start_time 列,所以您可以使用 row_number() 这是Window Functions of PostgreSQL 之一并将其堆叠在

start_time 的顺序,如果您期望第一个 start_time 的行值

从 (SELECT event_id ,ROW_NUMBER() OVER(PARTITION BY event_id ORDER BY start_time) AS first_row FROM Rsvp) 其中 first_row = 1

如果您希望行值与最后一个 start_time 相同,则 start_time 的顺序相反

从 (SELECT event_id ,ROW_NUMBER() OVER(PARTITION BY event_id ORDER BY start_time desc) AS last_row FROM Rsvp) 其中 last_row = 1

您还可以根据自己的要求使用不同的Window Function。

【讨论】:

【参考方案4】:

操作的句法与逻辑顺序

我认为,只有了解了logical order of operations in SQL,才能真正了解relationship between DISTINCT and ORDER BY(或GROUP BY,就此而言)的困惑。它不同于操作的句法顺序,后者是混淆的主要来源。

在这个例子中,考虑到它的句法接近性,它看起来好像DISTINCTSELECT相关,但它实际上是一个在之后应用的运算符@ 987654335@(投影)。由于DISTINCT 的性质(删除重复行),在DISTINCT 操作(包括ORDER BY 子句)之后 行的所有未投影内容不再可用.根据logical order of operations (simplified):

FROM(产生所有可能的列引用) WHERE(可以使用来自FROM的所有列引用) SELECT(可以使用来自FROM的所有列引用,并创建新的表达式,并为它们取别名) DISTINCT(对SELECT投影的元组进行操作) ORDER BY(取决于DISTINCT的存在,可以对SELECT投影的元组进行操作,如果DISTINCT不存在*也许(取决于方言)也可以在其他表达式上)

DISTINCTORDER BY 呢?

事实上,没有DISTINCTORDER BY 也可以访问(在某些方言中)没有被预测的东西可能有点奇怪,当然有用。例如。这行得通:

WITH emp (id, fname, name) AS (
  VALUES (1, 'A', 'A'),
         (2, 'C', 'A'),
         (3, 'B', 'B')
)
SELECT id
FROM emp
ORDER BY fname DESC

dbfiddle here。制作

id
--
2
3
1

当您添加 DISTINCT 时,这会发生变化。这不再有效:

WITH emp (id, fname, name) AS (
  VALUES (1, 'A', 'A'),
         (2, 'C', 'A'),
         (3, 'B', 'B')
)
SELECT DISTINCT name
FROM emp
ORDER BY fname DESC

dbfiddle here。错误是:

错误:对于 SELECT DISTINCT,ORDER BY 表达式必须出现在选择列表中 第 8 行:按 fname DESC 排序

因为fname 的值是什么,您会将name = A 赋予什么? A 还是 C?答案将决定您是得到AB 还是BA。无法决定。

PostgreSQL DISTINCT ON

现在,正如above linked article 中所述,PostgreSQL 支持对此的例外情况,这有时会很有用:DISTINCT ON(另见questions like these):

WITH emp (id, fname, name) AS (
  VALUES (1, 'A', 'A'),
         (2, 'C', 'A'),
         (3, 'B', 'B')
)
SELECT DISTINCT ON (name) id, fname, name
FROM emp
ORDER BY name, fname, id

dbfiddle here,制作:

id |fname|name
---|-----|----
1  |A    |A   
3  |B    |B   

此查询只允许生成 name 的不同值,然后对于每个重复的行,取第一个给定 ORDER BY 子句的值,这使得每个不同组的选择明确。 This can be emulated in other RDBMS using window functions.

【讨论】:

【参考方案5】:

我能够通过在我的选择中添加一列然后在该列上使用ORDER BY 来解决此错误。我有SELECT DISTINCT concat(dl.FirstName, concat(' ', dl.LastName)) as displayName, ...,我想ORDER BY 姓氏(就像一个人一样)。我尝试了所有我能想到的在SELECT 中添加ORDER BY 的排列方式,但有效的方法是添加, dl.LastName as lastName,然后在查询末尾添加ORDER BY lastName ASC,就像在更典型的查询中一样。

我最终得到了一个额外的列,但在我的应用程序中很容易被忽略。

【讨论】:

以上是关于PG::Error: SELECT DISTINCT, ORDER BY 表达式必须出现在选择列表中的主要内容,如果未能解决你的问题,请参考以下文章

PG::Error: 错误: 列 tutorials.tutorialcategory_id 不存在

sql distinc con conchasoloaño

Rails ActiveRecord:PG::Error:错误:列引用“created_at”不明确

尝试部署到 heroku 时出错(按照 ror 教程)PG::Error: ERROR: column "password_digest" of relationship &quo

Rails 报告找不到存在的列

Spark入门--Spark的intersectionsubtractunion和distinc