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

niwlg2el  于 2023-06-22  发布在  Ruby
关注(0)|答案(6)|浏览(253)

ActionView::模板::错误(PG::错误:错误:对于SELECT DISTINCT,ORDER BY表达式必须出现在选择列表中
我正在创建一个事件网站,我试图排序的事件的开始时间呈现的回复。有很多的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') %>
rqdpfwrv

rqdpfwrv1#

我知道这是一个相当老的问题,但我刚刚在脑海中经历了一个小例子,它帮助我理解了为什么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上的显式分组和聚合允许数据库对结果元组进行明确的排序。但是请注意,在这种情况下,可读性肯定是一个问题;)

lbsnaicq

lbsnaicq2#

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构造被减少,因为它只有单列。

rslzwgfq

rslzwgfq3#

语法与逻辑操作顺序

我认为,围绕relationship between DISTINCT and ORDER BY(或GROUP BY,就此而言)的混乱,只有在理解logical order of operations in SQL的情况下才能真正理解。它不同于操作的句法顺序,这是混淆的主要来源。
在这个例子中,DISTINCT看起来好像与SELECT相关,因为它的语法很接近,但它实际上是一个在SELECT(投影)之后应用的运算符。由于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指定
因为fname的值是什么?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

neekobn8

neekobn84#

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

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

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

  • start_time的逆序,如果您期望的行值为last 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)其中last_row = 1
您也可以根据需要使用不同的Window Function

r55awzrz

r55awzrz5#

这个GitHub的答案给了我一个我在这里发布的其他答案中找不到的洞察力。
总之,您需要与SELECT语句中的ORDER BY语句具有相同的逻辑。
在给出的示例中,他们使用CASE语句来定制订单。
然而,在示例中,它们有两次CASE语句,这是不必要的,SQL可以简化为:

SELECT DISTINCT pet.name, pet.alive, CASE WHEN pet.alive THEN 1 ELSE 0 
  END AS custom_order
    FROM (values ('a', true), ('b', true), ('c', false)) AS pet(name, alive)
   ORDER BY custom_order;

假设这是Rails中的一个模型,则示例可以写成:

Pet.select(:name, :alive, Arel.sql("CASE WHEN alive THEN 1 ELSE 0 
  END AS custom_order")).order(:custom_order)

如果排序顺序上不需要自定义名称,ActiveRecord会将其命名为case,这也可以用于排序

Pet.select(:name, :alive, Arel.sql("CASE WHEN alive THEN 1 ELSE 0 
  END")).order(:case)
suzh9iv8

suzh9iv86#

我可以通过在select中添加一列,然后在该列上使用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,就像在一个更典型的查询中一样。
最后我增加了一列,但在我的应用程序中很容易忽略它。

相关问题