Rails:用空值sorting最后

在我的Rails应用程序中,我偶然遇到了一个问题,我想知道其他人是如何解决的:

我有某些logging的值是可选的,所以一些logging有一个值,有些为该列的空值。

如果我在某些数据库上按该列sorting,那么首先对这些零值进行sorting,然后在某些数据库上进行sorting。

例如,我有可能或可能不属于一个集合的照片,即有一些照片,其中collection_id=nil和一些collection_id=1等。

如果我做Photo.order('collection_id desc)然后在SQLite上,我得到了空值,但在PostgreSQL上,我得到了空值。

有没有一个好的,标准的Rails方式来处理这个问题,并在任何数据库中获得一致的性能?

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

 @nonull = Photo.where("collection_id is not null").order("collection_id desc") @yesnull = Photo.where("collection_id is null") @wanted = @nonull+@yesnull 

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

我不是SQL的专家,但为什么不先sorting,如果什么东西是空的,然后按您想要如何sorting它。

 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 

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

 Photo.order('collection_id DESC NULLS LAST') #Null's Last Photo.order('collection_id DESC NULLS FIRST') #Null's First 

但SQLite3会给你错误。

在column_name前面加上减号 ,并颠倒顺序方向。 它在MySQL上工作。 更多细节

 Product.order('something_date ASC') # NULLS came first Product.order('-something_date DESC') # NULLS came last 
 Photo.order('collection_id DESC NULLS LAST') 

我知道这是一个旧的,但我只是发现这个片段,它适用于我。

表演晚了点,但有一个通用的SQL方式来做到这一点。 像往常一样, CASE来救援。

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

即使现在是2017年,对于NULL是否应该优先考虑还没有达成共识。 如果没有你明确的说明,你的结果将会根据DBMS而变化。

该标准没有指定如何将NULL与非NULL值进行sorting,除了任何两个NULL被认为是相同的sorting,并且NULL应该sorting高于或低于所有非NULL值。

来源,比较大多数DBMSs

为了说明这个问题,我编写了一些关于Rails开发的最stream行的例子:

PostgreSQL的

NULL s的值最高。

默认情况下,空值sorting就像大于任何非空值一样。

来源:PostgreSQL文档

MySQL的

NULL s的值最低。

在执行ORDER BY时,如果执行ORDER BY … ASC,则首先显示NULL值,如果执行ORDER BY … DESC,则显示NULL值。

来源:MySQL文档

SQLite的

NULL s的值最低。

具有NULL值的行高于具有常规值的行,并且按降序排列。

资源

不幸的是,Rails本身并没有提供解决scheme。

PostgreSQL的具体

对于PostgreSQL,你可以很直观地使用:

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

MySQL的具体

对于MySQL,你可以预先设定减号,但这个function似乎没有logging。 似乎不仅用数值工作,而且用date工作。

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

PostgreSQL和MySQL的具体

为了涵盖他们两个,这似乎工作:

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

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

通用解决scheme

为了为所有的DBMS提供交叉支持,你必须用@PhilITbuild议的CASE来写一个查询:

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

这意味着首先按CASE结果sorting每个logging(默认升序,这意味着NULL值将是最后一个),其次是calculation_id

最简单的方法是使用:

.order('name nulls first')

为了后人的缘故,我想强调一下与NULLS FIRST有关的ActiveRecord错误。

如果您尝试拨打:

 Model.scope_with_nulls_first.last 

Rails将尝试调用reverse_order.first ,而reverse_orderNULLS LAST不兼容,因为它会尝试生成无效的SQL:

 PG::SyntaxError: ERROR: syntax error at or near "DESC" LINE 1: ...dents" ORDER BY table_column DESC NULLS LAST DESC LIMIT... 

这是几年前在一些尚未解决的Rails问题( 一 , 二 , 三 )中提到的。 我能够通过执行以下操作来解决此问题:

  scope :nulls_first, -> { order("table_column IS NOT NULL") } scope :meaningfully_ordered, -> { nulls_first.order("table_column ASC") } 

看起来,通过将这两个命令链接在一起,生成了有效的SQL:

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

唯一的缺点是这个链接必须在每个范围内完成。

在我的情况下,我需要通过ASC的开始和结束datesorting行,但在一些情况下,end_date是空的,行应该在上面,我用

@invoice.invoice_lines.order('start_date ASC, end_date ASC NULLS FIRST')

如果你想要在数据库types中保持一致的结果,你似乎必须在Ruby中这样做,因为数据库本身解释了NULLS是否在列表的前面或末尾。

 Photo.all.sort {|a, b| a.collection_id.to_i <=> b.collection_id.to_i} 

但是这不是很有效率。