70

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') %>
4

5 回答 5

99

我知道这是一个相当古老的问题,但我只是在脑海中经历了一个小例子,它帮助我理解了为什么 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 的答案的替代方法是使用聚合函数,如MINorMAX进行排序:

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

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

于 2013-08-21T20:40:23.567 回答
82

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 构造被减少的单列情况下按预期工作,因为它只是单列。

于 2012-10-02T15:25:14.507 回答
2

因为,您使用的是 start_time 列,所以您可以使用PostgreSQL 的窗口函数之一的 row_number() 并将其堆叠在

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

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

  • start_time 的相反顺序,如果您期望最后一个 start_time 的行值

    选择 event_id from (SELECT event_id ,ROW_NUMBER() OVER(PARTITION BY event_id ORDER BY start_time desc) AS last_row FROM Rsvp) where last_row = 1

您还可以根据需要使用不同的窗口功能

于 2019-06-24T09:46:57.117 回答
2

操作的句法与逻辑顺序

我认为只有理解了 SQL 中操作的逻辑顺序,才能真正理解(或者,就此而言)之间关系DISTINCTORDER BY的混淆。它不同于操作的句法顺序,后者是混淆的主要来源。GROUP BY

在这个例子中,考虑到它的句法接近性,它看起来好像DISTINCT与 相关,但它实际上是一个在(投影)之后SELECT应用的运算符。由于 what does(删除重复行)的性质,在操作(包括该子句)之后,行的所有未投影内容都不再可用。根据操作的逻辑顺序(简化) SELECTDISTINCTDISTINCTORDER BY

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

关于DISTINCTORDER BY

事实上,没有DISTINCT,ORDER 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 在这里。生产

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 在这里。错误是:

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

因为你fname会赋予什么价值name = AA还是C?答案将决定您是否会因此得到A,BB, A。无法决定。

PostgreSQLDISTINCT ON

现在,正如上面链接的文章中提到的,PostgreSQL 支持一个例外,这偶尔会很有用:(DISTINCT ON另请参阅这些问题):

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 这里,产生:

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

此查询只允许生成 的不同值name,然后对每个重复行取第一个给定ORDER BY子句的值,这使得每个不同组的选择是明确的。这可以在其他 RDBMS 中使用窗口函数来模拟

于 2021-10-05T15:11:51.077 回答
-1

我能够通过在我的选择中添加一列然后ORDER BY在该列上使用来解决此错误。我有SELECT DISTINCT concat(dl.FirstName, concat(' ', dl.LastName)) as displayName, ...并且我想ORDER BY姓氏(就像一个人一样)。我尝试了所有我能想到的ORDER BY在.SELECT, dl.LastName as lastNameORDER BY lastName ASC

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

于 2021-06-25T16:12:35.310 回答