ORDER BY有时使用Active Record&导轨

时间:2016-04-12 17:32:21

标签: sql ruby-on-rails postgresql activerecord

在我的rails应用程序中(使用postgresql),我正在尝试撰写一个Active Record查询来查找一组志愿者记录,然后按first_name,然后last_name,然后{{}进行排序。 1}}。此外,emailfirst_name可能是last_name(两者都是null或两者都不是null)。例如,我希望按照以下列表进行排序:

  1. 志愿者[first_name:'Alex',last_name:'Diego',电子邮件:'a.diego@person.com']
  2. 志愿者[first_name:null,last_name:null,电子邮件:'cxxr@person.com']
  3. 志愿者[first_name:'Josh',last_name:'Broger',电子邮件:'broger@person.com']
  4. 志愿者[first_name:'Josh',last_name:'Broger',电子邮件:'jcool@person.com']
  5. 志愿者[first_name:'Josh',last_name:'Kenton',电子邮件:'aj@person.com']
  6. 最初,我有以下代码:

    null

    此代码有效,但结果由志愿者分组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_name首先,其他志愿者最后只有last_name(因此在上面的示例列表中,志愿者#2将是最后一个)。 this helpful post的答案表明我应该在语句的email部分使用COALESCE()函数来获得我想要的结果。真棒!所以我将我的代码更新为以下内容:

    ORDER BY

    问题是此代码现在返回

    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" (删除sql的.uniq部分),当我这样做时,新代码运行没有错误,但是结果没有正确排序:它们的排序方式与原始代码相同(不含DISTINCT的代码。)

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

    非常感谢任何帮助!!

    更新& ANSWER

    在获得Kristján的宝贵帮助以及下面的答案后,我解决了原因是多方面的问题:

    1. COALESCE()添加到ActiveRecord查询时,会将.uniq添加到发送到数据库的sql中。 DISTINCT有一些比SELECT DISTINCT更严格的要求。正如Kristján和described in more detail in this S.O. answer所指出的,SELECT表达式必须与最左边的DISTINCT表达式匹配。当我使用包含ORDER BY的sql片段更新.order()时,我还需要使用COALESCE()将匹配的sql片段添加到语句的SELECT部分。
    2. 上面的
    3. 1只是删除了我得到的错误。此时,我的查询正在运行,但结果的排序与使用.select()之前的结果相同。 Kristján在下面的答案中提供了正确的描述,但事实证明我的查询运行正常,只是COALESCE()在任何小写之前将任何大写排序。所以“Z”将在“a”前排序。通过使用COALESCE()添加将COALESCE()字段转换为小写的函数,可以解决此问题。
    4. 以下是我的回答:

      LOWER()

      注意:

      当我稍后在查询上调用 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()片段的volunteers_count模型添加自定义User方法。

2 个答案:

答案 0 :(得分:2)

您正在遇到一个信箱问题:您的名字全部大写,但电子邮件是小写的,并且对于大多数排序规则,大写字母都是小写字母。看看这个简单的例子:

#= 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"))')

答案 1 :(得分:-1)

把它扔出去但是你试过(mysql)

ORDER BY志愿者ASC THEN last_name ASC THEN电子邮件ASC

(希望将此作为评论但不足以代表:((() 如果这不好,请只是评论,所以我可以把它拿下来,所以我不会失去代表:)