Rails:最后以空值排序

And*_*rew 77 sorting null activerecord ruby-on-rails ruby-on-rails-3

在我的Rails应用程序中,我遇到了几次问题,我想知道其他人如何解决:

我有一些值是可选的记录,所以有些记录有一个值,有些记录的值为null.

如果我在某些数据库上按该列排序,则首先对空值进行排序,在某些数据库上对空值进行排序.

例如,我有照片可能属于或可能不属于集合,即有一些照片在哪里collection_id=nil和一些在哪里collection_id=1等.

如果我Photo.order('collection_id desc)在SQLite上做的话我最后得到空值但是在PostgreSQL上我先得到空值.

是否有一个很好的标准Rails方法来处理这个并在任何数据库中获得一致的性能?

Int*_*tss 247

我不是SQL的专家,但为什么不先排序,如果某事先是null,那么按你想要的排序方式排序.

Photo.order('collection_id IS NULL, collection_id DESC')  # Null's last
Photo.order('collection_id IS NOT NULL, collection_id DESC') # Null's first
Run Code Online (Sandbox Code Playgroud)

如果您只使用PostgreSQL,您也可以这样做

Photo.order('collection_id DESC NULLS LAST')  #Null's Last
Photo.order('collection_id DESC NULLS FIRST') #Null's First
Run Code Online (Sandbox Code Playgroud)

如果你想要一些通用的东西(比如你在几个数据库中使用相同的查询,你可以使用(由@philT提供)

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
Run Code Online (Sandbox Code Playgroud)

  • +1,比接受的答案要好得多,可以通过Arel表达 (18认同)
  • 请注意,`NULLS LAST'不允许您在Rails 4.2中将`.last()`链接到查询中.我在下面发布了一个解决方案. (2认同)
  • 为什么按“IS NULL”排序会将 NULL 行放在最后?你可能会认为这会把他们放在第一位。 (2认同)
  • @jackocr myguess:如果为true,则IS NULL计算结果为1,如果为false则为0,排序,0在1之前... (2认同)

Ada*_*bik 31

即使现在是2017年,关于NULLs 是否应该优先,仍然尚未达成共识.如果没有明确说明,您的结果将根据DBMS而有所不同.

该标准没有指定与非NULL值相比应该如何排序NULL,除了要将任何两个NULL视为等同排序,并且NULL应该在所有非NULL值之上或之下排序.

来源,大多数DBMS的比较

为了说明这个问题,我在Rails开发时编制了一些最常见的案例列表:

PostgreSQL的

NULLs具有最高价值.

默认情况下,null值排序为大于任何非null值.

来源:PostgreSQL文档

MySQL的

NULLs的价值最低.

在执行ORDER BY时,如果您执行ORDER BY ... ASC,则首先显示NULL值,如果执行ORDER BY ... DESC,则返回最后一个值.

来源:MySQL文档

SQLite的

NULLs的价值最低.

具有NULL值的行高于具有常规值的行按升序排列,并且按降序顺序颠倒.

资源

不幸的是,Rails本身并没有为它提供解决方案.

PostgreSQL具体

对于PostgreSQL,您可以非常直观地使用:

Photo.order('collection_id DESC NULLS LAST') # NULLs come last

MySQL具体

对于MySQL,您可以预先设置减号,但此功能似乎没有记录.似乎不仅可以使用数值,还可以使用日期.

Photo.order('-collection_id DESC') # NULLs come last

PostgreSQL和MySQL具体

为了覆盖它们,这似乎有效:

Photo.order('collection_id IS NULL, collection_id DESC') # NULLs come last

不过,这个在SQLite中不起作用.

通用解决方案

要为所有DBMS提供交叉支持,您必须使用CASE@PhilIT建议的查询:

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')

这意味着首先按CASE结果对每个记录进行排序(默认为升序,这意味着NULL值将是最后一个),其次是calculation_id.


ray*_*ibi 12

在column_name前放置减号并反转订单方向.它适用于mysql.更多细节

Product.order('something_date ASC') # NULLS came first
Product.order('-something_date DESC') # NULLS came last
Run Code Online (Sandbox Code Playgroud)


Tho*_*cey 12

Photo.order('collection_id DESC NULLS LAST')
Run Code Online (Sandbox Code Playgroud)

我知道这是一个旧的,但我刚刚找到这个片段,它对我有用.


Dav*_*les 9

Rails 6.1向 Arel for PostgreSQL添加了nulls_firstnulls_last方法。

例子:

User.order(User.arel_table[:login_count].desc.nulls_last)
Run Code Online (Sandbox Code Playgroud)

来源:https://www.bigbinary.com/blog/rails-6-1-adds-nulls-first-and-nulls-last-to-arel


Phi*_*ilT 8

在节目的后期,但有一个通用的SQL方法来做到这一点.像往常一样,CASE去救援.

Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢,像魅力一样! (2认同)

Lan*_*ose 6

为了后人的缘故,我想突出显示与ActiveRecord相关的错误NULLS FIRST.

如果你试着打电话:

Model.scope_with_nulls_first.last
Run Code Online (Sandbox Code Playgroud)

Rails尝试生成无效的SQL时会尝试调用reverse_order.first,但reverse_order不兼容NULLS LAST:

PG::SyntaxError: ERROR:  syntax error at or near "DESC"
LINE 1: ...dents"  ORDER BY table_column DESC NULLS LAST DESC LIMIT...
Run Code Online (Sandbox Code Playgroud)

这是几年前在一些仍然开放的Rails问题(,,)中引用的.通过执行以下操作,我能够解决它:

  scope :nulls_first, -> { order("table_column IS NOT NULL") }
  scope :meaningfully_ordered, -> { nulls_first.order("table_column ASC") }
Run Code Online (Sandbox Code Playgroud)

似乎通过将两个订单链接在一起,生成了有效的SQL:

Model Load (12.0ms)  SELECT  "models".* FROM "models"  ORDER BY table_column IS NULL DESC, table_column ASC LIMIT 1
Run Code Online (Sandbox Code Playgroud)

唯一的缺点是必须为每个范围进行链接.


小智 5

最简单的方法是使用:

.order('name nulls first')

  • 这对Po​​stgres来说是最好的 (3认同)

Eri*_*ric -4

将数组添加在一起将保持顺序:

@nonull = Photo.where("collection_id is not null").order("collection_id desc")
@yesnull = Photo.where("collection_id is null")
@wanted = @nonull+@yesnull
Run Code Online (Sandbox Code Playgroud)

http://www.ruby-doc.org/core/classes/Array.html#M000271

  • 这是一个坏主意,因为 `where` 不返回数组,它返回 ActiveRecord::Relation 并将结果强制放入数组将导致所有期望标准 ActiveRecord::Relation 的操作失败(如分页)。 (11认同)
  • 嗯,我不喜欢这个主意,但我认为这会起作用。抱歉这么久才开放,我希望能出现其他一些答案。不过,花了一些时间思考之后,我认为这可以做成照片模型上的一种方法,这样就不会感觉太糟糕了。 (2认同)