在我的rails应用程序中(使用postgresql),我正在尝试撰写一个Active Record查询来查找一组志愿者记录,然后按first_name
,然后last_name
,然后{{}进行排序。 1}}。此外,email
和first_name
可能是last_name
(两者都是null
或两者都不是null
)。例如,我希望按照以下列表进行排序:
null
,last_name:null
,电子邮件:'cxxr@person.com'] 最初,我有以下代码:
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()
不是我问题的正确解决方案)。
非常感谢任何帮助!!
在获得Kristján的宝贵帮助以及下面的答案后,我解决了原因是多方面的问题:
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
部分。.select()
之前的结果相同。 Kristján在下面的答案中提供了正确的描述,但事实证明我的查询运行正常,只是COALESCE()
在任何小写之前将任何大写排序。所以“Z”将在“a”前排序。通过使用COALESCE()
添加将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
方法。
答案 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.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"))')
答案 1 :(得分:-1)
把它扔出去但是你试过(mysql)
ORDER BY志愿者ASC THEN last_name ASC THEN电子邮件ASC
(希望将此作为评论但不足以代表:((() 如果这不好,请只是评论,所以我可以把它拿下来,所以我不会失去代表:)