在我的 rails 应用程序(使用 postgresql)中,我试图编写一个 Active Record 查询来查找一组志愿者记录,然后按 first_name
对它们进行排序。 ,然后 last_name
,然后 email
.此外,first_name
和 last_name
可能是 null
(两者都将是 null
或两者都不是 null
)。例如,我希望对以下列表进行排序:
null
, 姓氏:null
, 电子邮件:'cxxr@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_name
& last_name
首先,其他志愿者只有email
最后(因此在上面的示例列表中,志愿者 #2 将是最后一个)。 this helpful post的答案表示我应该使用 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"
我在没有 .uniq
的情况下测试了我的新代码(删除 sql 的 DISTINCT
部分),当我这样做时,新代码运行没有错误,但是结果没有正确排序:它们的排序方式与我的原始代码相同(没有 COALESCE()
的代码) )。我想我犯了一个语法错误,但我无法弄清楚它是什么(或者我可能错了,
COALESCE()
不是我的问题的正确解决方案)。任何帮助是极大的赞赏!!
更新和回答
从 Kristján 得到宝贵帮助后以及他在下面的回答,我解决了原来是多个问题的问题:
.uniq
到 ActiveRecord 查询,它添加 DISTINCT
到发送到数据库的 sql。 SELECT DISTINCT
有一些比简单的更严格的要求 SELECT
.正如 Kristján 和 described in more detail in this S.O. answer 所指出的那样, DISTINCT
表达式必须匹配最左边的 ORDER BY
表达式。当我更新 .order()
我的 sql 片段包括 COALESCE()
,我还需要在SELECT
中添加一个匹配的sql片段声明的一部分 .select()
. COALESCE()
之前的结果相同。 . Kristján 在下面的回答中提供了正确的描述,但事实证明我的查询运行正确,只是 COALESCE()
在任何小写字母之前对任何大写字母进行排序。所以“Z”将排在“a”之前。这个问题可以通过添加一个函数来解决COALESCE()
字段使用 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
没有使用 .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.com
排序后 Josh
.为避免这种情况,您可以按小写值排序。这是您拥有的数据的简单版本:#= 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"))')
关于sql - 使用 Active Record 和 Rails 有时为空的 ORDER BY 列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36580266/