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

And*_*ndy 45 ruby postgresql ruby-on-rails distinct ruby-on-rails-3

ActionView :: Template :: Error(PG ::错误:错误:对于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') %>
Run Code Online (Sandbox Code Playgroud)

Adr*_*oKF 70

我知道这是一个相当古老的问题,但我只是通过一个小例子来帮助我理解为什么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
Run Code Online (Sandbox Code Playgroud)

现在,您想要获取由各自的start_times排序的不同event_ids的列表.但应该1去哪里?它应该是第一个,因为一个元组在1970年1月1日开始,还是应该因为2013年8月21日而持续?

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

至于实际的问题 - 马修的答案的另一种选择是使用类似MINMAX用于排序的聚合函数:

  SELECT event_id
    FROM Rsvp
GROUP BY event_id
ORDER BY MIN(start_time)
Run Code Online (Sandbox Code Playgroud)

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

  • 此外,按 MIN 或 MAX 排序不起作用。它只是给出了同样的错误。 (3认同)
  • @AdrianoKF 我刚刚使用最新版本的 MySQL `mysql-8.0.2-dmr-winx64` (开发版本)进行了测试。现在 MySQL 中确实存在错误,就像 PostgreSQL 中一样。无论如何,使用 MIN 和 MAX 作为解决方法的查询在 PostgreSQL 中也不起作用。 (2认同)

Mat*_*ood 57

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 ...
Run Code Online (Sandbox Code Playgroud)

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

  • 哇你真棒!很好的解释,你刚刚为我的生活增加了几天:D (4认同)
  • "从逻辑上讲,如果你只想要一个不同的event_id值列表,它们发生的顺序应该是无关紧要的." - 我已经看过一个应用程序,其中有顺序和限制,所以假设只选择10个第一个和不同的项目(在这种情况下是事件). (4认同)

Luk*_*der 7

操作的语法顺序与逻辑顺序

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

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

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

DISTINCT和呢?ORDER 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
Run Code Online (Sandbox Code Playgroud)

dbfiddle 在这里。生产

id
--
2
3
1
Run Code Online (Sandbox Code Playgroud)

当您添加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
Run Code Online (Sandbox Code Playgroud)

dbfiddle 在这里。错误是:

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

因为fname你会赋予什么价值name = AA或者C?答案将决定您是否会得到A,B结果或B, 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
Run Code Online (Sandbox Code Playgroud)

dbfiddle 在这里,产生:

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

Run Code Online (Sandbox Code Playgroud)

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