[英]ORDER BY columns that are sometimes empty using Active Record & Rails
在我的Rails应用程序中(使用postgresql),我试图组成一个Active Record查询来查找一组志愿者记录,然后按first_name
, last_name
,然后按email
进行last_name
。 此外, 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自愿者是最后一名)。 这篇有用的帖子的答案表明,我应该在语句的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)及其以下答案中获得了宝贵的帮助后,我解决了原来是多个问题:
.uniq
添加到ActiveRecord查询时,它将DISTINCT
添加到要发送到数据库的sql中。 SELECT DISTINCT
比简单的SELECT
有一些更严格的要求。 正如克里斯蒂安(Kristján)指出并在该SO答案中更详细地描述的 , DISTINCT
表达式必须与最左边的ORDER BY
表达式匹配。 当我用包含COALESCE()
sql片段更新.order()
时,我还需要使用.select()
将匹配的sql片段添加到语句的SELECT
部分。 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.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"))')
只是将它扔在那里,但您尝试过(mysql)
ORDER BY志愿者ASC THEN姓氏ASC THEN电子邮件ASC
(想对此发表评论,但代表不足:((()如果这不好,请发表评论,这样我就可以把它记下来,这样我就不会失去代表:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.