【问题标题】:Rails: Sort nils to the end of a scope?Rails:将 nil 排序到范围的末尾?
【发布时间】:2011-04-02 02:04:03
【问题描述】:

所以,我的照片模型中有以下范围:

scope :best, order(:average_rating.desc)

唯一的问题是,评分是事后添加到模型中的,因此生产应用程序有很多记录,其中average_rating 为 nil。当我调用这个范围时,它首先返回所有的 nil——实际上它应该是相反的,nil 应该是最后的(它们是尚未评级的照片)。

如何将 nil 排序到此范围的末尾?

【问题讨论】:

    标签: sql ruby-on-rails ruby-on-rails-3 sorting rails-activerecord


    【解决方案1】:

    我玩游戏有点晚了,但又出现了这个问题,解决方案真的没那么难。

    一种可移植的解决方案是使用 CASE 将 NULL 转换为将要结束的东西。如果您的评分不是负数,那么 -1 是一个不错的选择:

    order('case when average_rating is null then -1 else average_rating end desc')
    

    使用 COALESCE 代替将在许多数据库中工作,并且比 CASE 的噪音要小得多:

    order('coalesce(average_rating, -1) desc')
    

    如果您想要 ASC 排序,那么上述方法会将 NULL 放在开头。如果你最后想要它们,那么你会使用比你的最高评级更大的东西,而不是 -1。例如,如果您对事物的评分为 1 到 5,则可以使用 11 来代替 NULL:

    order('case when average_rating is null then 11 else average_rating end asc')
    order('coalesce(average_rating, 11) asc')
    

    大多数人会使用 10,但有时您需要大声一点,所以我们使用 11。

    您不能真正依赖数据库将 NULL 放在开头或结尾,因此最好是明确的,即使您只是提醒未来的自己您已经处理了 NULL 的情况。

    【讨论】:

    • @Andrew:我应该等几个月才能迎来一周年纪念日 :)
    • 好像有更好的办法:order('average_rating IS NULL, average_rating DESC'),看看stackoverflow.com/a/7055259/496209
    • @Luksurious:问题在于没有记录和可移植的布尔排序。因此,要获得可靠的结果,请返回 case when average_rating is null then ...
    • @Luksurious:MySQL 将 1 和 0 用于布尔值,因此布尔值的排序与任何其他数字一样。 PostgreSQL 有本机布尔值,我从来没有为它们找到定义的排序顺序,所以它们可以工作,但不是“正式”的。 SQLite 将 1 和 0 用于布尔值,但 Rails(错误地)将 't''f' 字符串用于布尔值,因此布尔排序是数字排序或字符串排序,具体取决于上下文。其他数据库会做其他事情。布尔排序有点乱,所以我只是使用 CASE 并手动完成。
    【解决方案2】:

    试试这个:)

    scope :best, order("average_rating DESC NULLS LAST")
    

    【讨论】:

    • 出于好奇,因为我是一个完全的 SQL 菜鸟,这适用于所有 SQL 驱动程序吗?我需要它至少可以在 SQLite 和 PostgreSQL 上工作。
    • 好的,这在 SQLite 上不起作用,所以我不能使用它。仍在搜索。
    • 这是一个不错的解决方案,但似乎有很多问题。它有时会导致 SQL 错误,我认为这是这里提出的问题:github.com/rails/rails/issues/11571 不幸的是,Rails 团队刚刚关闭了该问题而没有修复它:\
    【解决方案3】:

    我知道这是不久前的事了,但是这对几个人来说都行不通

    order("ISNULL(average_rating) DESC")
    

    【讨论】:

    • 我认为 SQL Server 和 MySQL 都有(但不确定它们在两者中是否相同),PostgreSQL 没有,SQLite 也没有。
    • 是的,我相信你是对的......如果有一个 PostgreSQL 等价物会很高兴......还没有真正使用过那个数据库!
    • 有人把这个放在关于 ISNULL 的 postgreSQL 问题上 - stackoverflow.com/questions/2214525/…
    【解决方案4】:

    虽然coalesce SQL 方法是great,但如果您的数据库不支持这个,这里有一个所有数据库都应该支持的方法:

    order("-average_rating DESC")

    这将对您的记录进行排序并将NULL 放在末尾。

    【讨论】:

    • 这是唯一符合我预期的答案。执行order "-due_date desc, due_date" 之类的操作以按日期排序,最后使用 nils。
    【解决方案5】:

    好吧,我从来没有找到一种适用于 DB 驱动程序的方法,所以我只是强制所有以前为零的记录的值为零。这为我解决了这个问题,虽然它有点野蛮。当计时器用完时,我会接受这个,因为这是我使用的解决方案,但如果有人重新访问这个并希望在未来提供替代答案,我很乐意稍后接受不同的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-30
      相关资源
      最近更新 更多