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

2024-03-02

在我的 Rails 应用程序(使用 postgresql)中,我尝试编写一个 Active Record 查询来查找一组志愿者记录,然后按以下顺序对它们进行排序first_name, then last_name, then email。此外,first_name and last_name may be null(两者都将是null或者两者都不会null)。例如,我希望对以下列表进行排序:

  1. 志愿者 [名字:'Alex',姓氏:'Diego',电子邮件:'[电子邮件受保护] /cdn-cgi/l/email-protection']
  2. 志愿者 [名字:null, 姓:null, 电子邮件: '[电子邮件受保护] /cdn-cgi/l/email-protection']
  3. 志愿者 [名字:'Josh',姓氏:'Broger',电子邮件:'[电子邮件受保护] /cdn-cgi/l/email-protection']
  4. 志愿者 [名字:'Josh',姓氏:'Broger',电子邮件:'[电子邮件受保护] /cdn-cgi/l/email-protection']
  5. 志愿者 [名字:'Josh',姓氏:'Kenton',电子邮件:'[电子邮件受保护] /cdn-cgi/l/email-protection']

最初,我有以下代码:

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 将是最后一个)。答案是这篇有用的帖子 https://stackoverflow.com/questions/8120953/order-by-with-columns-that-are-sometimes-empty表明我应该使用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

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

我想象我犯了一个语法错误,但我无法弄清楚它是什么(或者也许我错了并且COALESCE()不是我的问题的正确解决方案)。

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

更新与解答

在获得宝贵的帮助后Kristján https://stackoverflow.com/users/802618/kristj%C3%A1n和他的回答如下,我解决了多个问题:

  1. 当你添加.uniq对于 ActiveRecord 查询,它添加DISTINCT发送到数据库的 sql。SELECT DISTINCT有一些比简单要求更严格的要求SELECT。正如 Kristján 和本 S.O. 中有更详细的描述。回答 https://stackoverflow.com/questions/9795660/postgresql-distinct-on-with-different-order-by, the DISTINCT表达式必须匹配最左边的ORDER BY表达式。当我更新的时候.order()我的 sql 片段包括COALESCE(),我还需要添加一个匹配的sql片段到SELECT 声明的一部分.select().
  2. 上面的 1 只是消除了我遇到的错误。此时,我的查询正在运行,但结果的排序方式与使用之前相同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')

Note:

当我稍后打电话时,我上面的回答实际上造成了另一个问题.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

所以你的查询实际上工作得很好,只是这样[email protected] /cdn-cgi/l/email-protection排序在Josh。为了避免这种情况,您可以按小写值排序。这是您拥有的数据的简单版本:

#= select * from volunteers;
 first_name | last_name |       email
------------+-----------+--------------------
 Josh       | Broger    | [email protected] /cdn-cgi/l/email-protection
 Josh       | Kenton    | [email protected] /cdn-cgi/l/email-protection
 ∅          | ∅         | [email protected] /cdn-cgi/l/email-protection
 Josh       | Broger    | [email protected] /cdn-cgi/l/email-protection
 Alex       | Diego     | [email protected] /cdn-cgi/l/email-protection

然后使用排序coalesce你在追求:

#= select * from volunteers order by lower(coalesce(first_name, email));
 first_name | last_name |       email
------------+-----------+--------------------
 Alex       | Diego     | [email protected] /cdn-cgi/l/email-protection
 ∅          | ∅         | [email protected] /cdn-cgi/l/email-protection
 Josh       | Broger    | [email protected] /cdn-cgi/l/email-protection
 Josh       | Broger    | [email protected] /cdn-cgi/l/email-protection
 Josh       | Kenton    | [email protected] /cdn-cgi/l/email-protection

或者对于您的完整版本,使用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"))')
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Active Record & Rails ORDER BY 有时为空的列 的相关文章

随机推荐

  • 用于单元/集成测试的嵌入式动物园管理员

    是否有嵌入式动物园管理员以便我们可以在单元测试中使用它 它可以与测试一起发货并开箱即用 也许我们可以模拟一些服务并注册到嵌入式动物园管理员 The Curator https github com Netflix curator wiki框
  • Mac 版 Github:缺少拉取请求按钮

    周六 我在 Mac 上安装了 Github Desktop 并尝试了 Github Workflow 创建一个分支 提交更改并执行拉取请求 一切顺利 今天我在工作中安装了 Mac 版 Github 但找不到 Pull Request 按钮
  • Typescript 在 vs 2015 ctp 6 中禁用保存时编译

    我需要知道如何禁用打字稿文件保存时编译 默认情况下启用 Typescript 编译 您可以执行下一步来禁用它 选择并单击 卸载项目 菜单项 选择已卸载的项目并单击 编辑 kproj 将新的 PropertyGroup 节点添加到项目根节点
  • 捆绑链接的 JavaScript 文件

    我正在使用 Visual Studio 2012 和 MVC4 我已将链接文件 来自另一个项目 添加到我的 MVC4 应用程序中 以下是该文件的属性 构建操作 内容 复制到输出目录 不复制 这是我的捆绑包的示例 bundles Add ne
  • WPF DataGrid AlternatingRowBackground 和 RowStyle 优先级

    我该如何做我的RowStyle后申请AlternatingRowBackground 我想要物品 有IsOrange as true具有Orange背景 无论交替的行背景如何 目前情况并非如此 XAML
  • 当选择文本时,如何用我自己的视图替换 UIMenuController?

    当选择文本时 默认情况下会弹出一个 UIMenuController 其中包含剪切 复制 粘贴等功能 我想用我自己的自定义视图替换它 外观相似 但高两倍 以便我可以有两行按钮 自定义视图 我怎样才能做到这一点 我知道没有简单的方法 我预计即
  • Mongodb动态like运算符

    在 mongodb 中相当于 sql like 运算符是 db users find shows m 使用 nodejs javascript 我想根据 url 参数动态更改字母 我努力了 letter req params letter
  • RxJava Observable.fromEmitter 奇怪的背压行为

    我一直在利用Observable fromEmitter 作为一个绝佳的替代品Observable create 我最近遇到了一些奇怪的行为 但我不太明白为什么会出现这种情况 我真的很感谢对背压和调度程序有一定了解的人来看看这个 publi
  • WebClient.DownloadString 速度慢?

    我正在开发一个使用 ASP Net 3 5 运行的 Web 应用程序 在应用程序的某个地方 我正在调用外部系统 此调用包括从特定 url 下载字符串 string targetUrl BuildMyUrl WebClient wc new
  • str(列表) 是如何工作的?

    为什么str list 返回我们如何在控制台上看到列表 如何str list 工作 任何对 CPython 代码的引用str list gt gt gt x abc def ghi gt gt gt str x abc def ghi 要从
  • 通过 C/C++ 程序使 LED 闪烁的步骤?

    通过 C C 程序制作带有 LED 闪光灯的小电路的最简单步骤是什么 我希望所需的依赖项和包数量最少 我应该将某些东西连接到哪个端口 我会使用哪个编译器 如何将数据发送到该端口 我需要微处理器吗 如果不是 我不想在这个简单的项目中使用它 编
  • 在 Jenkinsfile 中设置 SonarQube Webhook

    我正在尝试创建一个 Jenkins 多分支管道 在每次推送到 bitbucket 时 都会对项目的该分支执行 SonarQube 分析 Jenkins 正确地为每个分支创建新作业 并在 SonarQube 中创建一个新项目 并将分支名称附加
  • 表面和纹理之间的差异(SDL/一般)

    谁能用简单的语言向我解释一下纹理和表面有什么区别 我看到它用在SDL2 as SDL Surface and SDL Texture SDL Texture是从创建的SDL Surface它又是从图像 位图创建的 两者都是像素的集合 但我没
  • 静态 uint8_t 数组的输入过程和类型

    我目前正在尝试将 Arduino IDE 中的整数变量转换为静态 uint8 t 数组的值 我在用 include
  • .NET 如何定位我正在“使用”的命名空间的 dll?

    NET如何定位我所在命名空间的dllusing 是的 我们确实提到了 referene c program files 中的路径 但是在构建和部署之后以及当软件安装在某些用户的计算机上时 它可能与我 开发人员 提到的路径不同 我的意思是它可
  • 如何删除 Rails 中的索引

    我发现我的架构中有两个 survey id 列 这给我带来了一些问题 具体来说 我需要删除第二个索引 因为我不希望 Survey id 是唯一的 add index completions survey id name index comp
  • .NET Core IHttpContextAccessor 问题

    我有静态助手类 public static class Current public static string Host get return httpContextAccessor here 如何访问 Host 属性中的当前 HttpC
  • 在Lua中注册C++函数?

    我正在尝试在 Lua 中注册一个 C 函数 但出现此错误 CScript cpp 39 error argument of type int CScript lua State does not match int lua State ED
  • 为什么 requirejs 尝试加载 .map?

    我正在尝试从 CDN 具体来说是 cdnjs 加载脚本 并且在 requirejs 中您必须像这样保留扩展名 require config baseUrl static js paths underscore cdnjs cloudflar
  • 使用 Active Record & Rails ORDER BY 有时为空的列

    在我的 Rails 应用程序 使用 postgresql 中 我尝试编写一个 Active Record 查询来查找一组志愿者记录 然后按以下顺序对它们进行排序first name then last name then email 此外