繁体   English   中英

使用Active Record&Rails有时为空的ORDER BY列

[英]ORDER BY columns that are sometimes empty using Active Record & Rails

在我的Rails应用程序中(使用postgresql),我试图组成一个Active Record查询来查找一组志愿者记录,然后按first_namelast_name ,然后按email进行last_name 此外, first_namelast_name可以为null (两者都为null或都不为null )。 例如,我希望对下面的列表进行排序:

  1. 志愿者[名字:'Alex',姓氏:'Diego',电子邮件:'a.diego@person.com']
  2. 志愿者[FIRST_NAME: null ,姓氏: null ,电子邮件: 'cxxr@person.com']
  3. 志愿者[名字:'Josh',姓氏:'Broger',电子邮件:'broger@person.com']
  4. 志愿者[名字:'Josh',姓氏:'Broger',电子邮件:'jcool@person.com']
  5. 志愿者[名字:'Josh',姓氏:'Kenton',电子邮件:'aj@person.com']

最初,我有以下代码:

Volunteer.joins(:volunteer_lists).
  where("(volunteer_lists.organizer_id = ? AND organizer_type = 'Organization') OR
  (volunteer_lists.organizer_id IN (?) AND organizer_type = 'Collaborative')",
  self.organization.id, collaboratives).uniq.
  order(:first_name, :last_name, :email)

该代码有效,除了将结果按first_namelast_name首先的志愿者分组,将其他结果仅按email志愿者分组(因此,在上面的示例列表中,#2自愿者是最后一名)。 这篇有用的帖子的答案表明,我应该在语句的ORDER BY部分中使用COALESCE()函数以获得所需的结果。 真棒! 因此,我将代码更新为以下内容:

Volunteer.joins(:volunteer_lists).
  where("(volunteer_lists.organizer_id = ? AND organizer_type = 'Organization') OR
  (volunteer_lists.organizer_id IN (?) AND organizer_type = 'Collaborative')",
  self.organization.id, collaboratives).uniq.
  .order('COALESCE("volunteers"."first_name", "volunteers"."email") ASC, COALESCE("volunteers"."last_name", "volunteers"."email") ASC, "volunteers"."email" ASC')

问题在于此代码现在返回

PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list

在两个版本的代码上使用to_sql ,我发现它们完全相同,只是增加了COALESCE()函数。

原始的,有效的代码的to_sql

SELECT  "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1  [["id", 1]]
  => "SELECT DISTINCT \"volunteers\".* FROM \"volunteers\" INNER JOIN \"volunteer_list_connectors\" ON \"volunteer_list_connectors\".\"volunteer_id\" = \"volunteers\".\"id\" INNER JOIN \"volunteer_lists\" ON \"volunteer_lists\".\"id\" = \"volunteer_list_connectors\".\"volunteer_list_id\" WHERE ((volunteer_lists.organizer_id = 1 AND organizer_type = 'Organization') OR\n      (volunteer_lists.organizer_id IN (1) AND organizer_type = 'Collaborative'))  ORDER BY \"volunteers\".\"first_name\" ASC, \"volunteers\".\"last_name\" ASC, \"volunteers\".\"email\" ASC"

更新代码的to_sql (唯一的区别是在ORDER BY ):

SELECT  "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT 1  [["id", 1]]
  => "SELECT DISTINCT \"volunteers\".* FROM \"volunteers\" INNER JOIN \"volunteer_list_connectors\" ON \"volunteer_list_connectors\".\"volunteer_id\" = \"volunteers\".\"id\" INNER JOIN \"volunteer_lists\" ON \"volunteer_lists\".\"id\" = \"volunteer_list_connectors\".\"volunteer_list_id\" WHERE ((volunteer_lists.organizer_id = 1 AND organizer_type = 'Organization') OR\n      (volunteer_lists.organizer_id IN (1) AND organizer_type = 'Collaborative'))  ORDER BY COALESCE(\"volunteers\".\"first_name\", \"volunteers\".\"email\") ASC, COALESCE(\"volunteers\".\"last_name\", \"volunteers\".\"email\") ASC, \"volunteers\".\"email\" ASC"

我测试了不带.uniq新代码(以删除sql的DISTINCT部分),当我这样做时,新代码运行没有错误,但是结果未正确排序:它们的排序方式与我的原始代码相同(不带COALESCE()的代码)。

我想我已经犯了一个语法错误,但是我无法弄清楚它是什么(或者我错了,而COALESCE()不是解决我问题的正确方法)。

任何帮助是极大的赞赏!!

更新和答案

在从克里斯蒂安(Kristján)及其以下答案中获得了宝贵的帮助后,我解决了原来是多个问题:

  1. .uniq添加到ActiveRecord查询时,它将DISTINCT添加到要发送到数据库的sql中。 SELECT DISTINCT比简单的SELECT有一些更严格的要求。 正如克里斯蒂安(Kristján)指出并在该SO答案中更详细地描述的DISTINCT表达式必须与最左边的ORDER BY表达式匹配。 当我用包含COALESCE() sql片段更新.order()时,我还需要使用.select()将匹配的sql片段添加到语句的SELECT部分。
  2. 上面的1只是删除了我遇到的错误。 那时,我的查询正在运行,但是结果的排序与使用COALESCE()之前的排序相同。 Kristján在下面的回答中提供了适当的描述,但事实证明我的查询运行正确,只是COALESCE()大写的内容排在小写的前面。 因此“ Z”将排在“ a”之前。 通过添加一个使用LOWER()COALESCE()字段转换为小写字母的函数可以解决此问题。

这是我的答案:

    Volunteer.select('LOWER(COALESCE("volunteers"."first_name", "volunteers"."email")), LOWER(COALESCE("volunteers"."last_name", "volunteers"."email")), LOWER("volunteers"."email"), "volunteers".*').
      joins(:volunteer_lists).
      where("(volunteer_lists.organizer_id = ? AND organizer_type = 'Organization') OR
      (volunteer_lists.organizer_id IN (?) AND organizer_type = 'Collaborative')",
      self.organization.id, collaboratives).uniq.
      order('LOWER(COALESCE("volunteers"."first_name", "volunteers"."email")) ASC, LOWER(COALESCE("volunteers"."last_name", "volunteers"."email")) ASC, LOWER("volunteers"."email") ASC')

注意:

当我稍后在查询中调用.count时,上述答案实际上创建了另一个问题。 .count因为自定义的中断.select()片段,我补充道。 为了解决这个问题,我需要向User模型中添加一个不使用.select()片段的自定义volunteers_count .select()

您遇到了字母大小写的问题:您的姓名全部大写,但电子邮件为小写,并且在大多数归类中,大写字母排在小写字母之前。 看看这个简单的例子:

#= select * from (values ('b'), ('B'), ('a'), ('A')) t (letter);
 letter
--------
 b
 B
 a
 A
#= select * from (values ('b'), ('B'), ('a'), ('A')) t (letter) order by letter;
 letter
--------
 A
 B
 a
 b

因此您的查询实际上运行良好,只是cxxr@person.comJosh之后排序。 为避免这种情况,可以按小写值排序。 这是您拥有的数据的简单版本:

#= select * from volunteers;
 first_name | last_name |       email
------------+-----------+--------------------
 Josh       | Broger    | jcool@person.com
 Josh       | Kenton    | aj@person.com
 ∅          | ∅         | cxxr@person.com
 Josh       | Broger    | broger@person.com
 Alex       | Diego     | a.diego@person.com

然后使用coalesce后的内容进行排序:

#= select * from volunteers order by lower(coalesce(first_name, email));
 first_name | last_name |       email
------------+-----------+--------------------
 Alex       | Diego     | a.diego@person.com
 ∅          | ∅         | cxxr@person.com
 Josh       | Broger    | broger@person.com
 Josh       | Broger    | jcool@person.com
 Josh       | Kenton    | aj@person.com

或使用ActiveRecord的完整版本:

Volunteer
  .joins(:volunteer_lists)
  .where(
    "(volunteer_lists.organizer_id = ? AND organizer_type = 'Organization') OR (volunteer_lists.organizer_id IN (?) AND organizer_type = 'Collaborative')",
    organization.id, collaboratives
  )
  .order('LOWER(COALESCE("volunteers"."first_name", "volunteers"."last_name", "volunteers"."email"))')

只是将它扔在那里,但您尝试过(mysql)

ORDER BY志愿者ASC THEN姓氏ASC THEN电子邮件ASC

(想对此发表评论,但代表不足:((()如果这不好,请发表评论,这样我就可以把它记下来,这样我就不会失去代表:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM