Ruby on rails Rails:最后为空的顺序
在我的Rails应用程序中,我曾多次遇到一个问题,我想知道其他人是如何解决的: 我有一些记录,其中值是可选的,所以有些记录有值,有些记录为空 如果我在某些数据库上按该列排序,空值将首先排序,在某些数据库上,空值将最后排序 例如,我有一些照片可能属于或不属于某个集合,即有些照片Ruby on rails Rails:最后为空的顺序,ruby-on-rails,ruby-on-rails-3,sorting,activerecord,null,Ruby On Rails,Ruby On Rails 3,Sorting,Activerecord,Null,在我的Rails应用程序中,我曾多次遇到一个问题,我想知道其他人是如何解决的: 我有一些记录,其中值是可选的,所以有些记录有值,有些记录为空 如果我在某些数据库上按该列排序,空值将首先排序,在某些数据库上,空值将最后排序 例如,我有一些照片可能属于或不属于某个集合,即有些照片Collection\u id=nil,有些照片Collection\u id=1等等 如果我做了Photo.order('collection\u id desc),那么在SQLite上我最后得到空值,但在PostgreS
Collection\u id=nil
,有些照片Collection\u id=1
等等
如果我做了Photo.order('collection\u id desc)
,那么在SQLite上我最后得到空值,但在PostgreSQL上我首先得到空值
有没有一种好的、标准的Rails方法来处理这个问题并在任何数据库中获得一致的性能?如果您想要在数据库类型中获得一致的结果,那么您似乎必须在Ruby中这样做,因为数据库本身会解释null是否位于列表的前端或末尾
Photo.all.sort {|a, b| a.collection_id.to_i <=> b.collection_id.to_i}
Photo.all.sort{a,b|a.collection_id.to|i b.collection_id.to|i}
但这不是很有效。将数组添加到一起将保持顺序:
@nonull = Photo.where("collection_id is not null").order("collection_id desc")
@yesnull = Photo.where("collection_id is null")
@wanted = @nonull+@yesnull
我不是SQL方面的专家,但为什么不先按空值排序,然后按您希望的排序方式排序呢
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
如果您想要通用的东西(比如您在多个数据库中使用相同的查询),您可以使用(由@philT提供)
最简单的方法是使用:
.order('name nulls first')
将减号放在列名称前面,并反转顺序方向。它在mysql上工作
虽然演出有点晚,但有一种通用的SQL方法可以做到。像往常一样,
CASE
来拯救观众
Photo.order('CASE WHEN collection_id IS NULL THEN 1 ELSE 0 END, collection_id')
为了子孙后代的利益,我想首先强调一个与
null相关的ActiveRecord错误
如果您尝试拨打:
Model.scope_with_nulls_first.last
Rails将尝试调用反向顺序。first
,反向顺序
与NULLS 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按开始日期和结束日期对行进行排序,但在少数情况下,结束日期为空,并且行应该在上面,我使用
@invoice.invoice\u line.order('start\u date ASC,end\u date ASC NULLS FIRST')
即使现在是2017年,对于是否应优先使用NULL
s仍然没有达成一致意见。如果不明确说明,结果将根据DBMS而有所不同
该标准没有规定空值与非空值相比应如何排序,除非任何两个空值被视为具有相同的顺序,并且空值的排序应高于或低于所有非空值
为了说明这个问题,我列出了几个最流行的Rails开发案例:
PostgreSQL
NULL
s的值最高
默认情况下,空值排序时好像大于任何非空值
MySQL
NULL
s的值最低
在执行“订购方式”时,如果执行“订购方式…”ASC,则首先显示空值,如果执行“订购方式…”DESC,则最后显示空值
数据库
NULL
s的值最低
具有空值的行按升序高于具有常规值的行,降序则相反
解决方案
不幸的是,Rails本身还没有提供解决方案
特定于PostgreSQL的
对于PostgreSQL,您可以非常直观地使用:
Photo.order('collection_id DESC NULLS LAST')#NULLS LAST
特定于MySQL的
对于MySQL,你可以在前面加上负号,但是这个功能似乎没有文档记录。它不仅适用于数值,也适用于日期
Photo.order('-collection_id DESC')#空值排在最后
特定于PostgreSQL和MySQL的
为了涵盖这两个方面,这似乎是可行的:
Photo.order('collection_id为NULL,collection_id DESC')#NULL最后出现
不过,这个在SQLite中不起作用
普遍解
要为所有DBMS提供交叉支持,您必须使用@PhilIT建议的CASE
编写查询:
Photo.order('collection\u id为NULL时为1,否则为0,collection\u id')
这转换为首先按大小写
结果对每个记录进行排序(默认情况下,升序表示NULL
值将是最后一个值)我不喜欢这个主意,但我认为这个主意行得通。很抱歉让它开了这么久,我希望会出现一些其他答案。不过,我花了更多的时间思考这个问题,我认为这可以成为照片模型上的一种方法,这样就不会感觉太糟糕了。我很容易找到答案如果您使用的是mysql。请参阅我的解决方案。好的,我应该提到我的方法是我能找到的唯一最不可知的方法。这是一个坏主意,因为在不返回数组的地方,它返回一个ActiveRecord::Relation,将结果强制放入数组将导致所有期望标准ActiveRecord::Relation的操作失败(比如分页)。没错,尽管可用的方法不是脱离AR就是(不完全)可移植。+1,比公认的答案好得多,可以通过Arelow表达,这是一个古老的评论!所以我猜我在尝试什么
scope :nulls_first, -> { order("table_column IS NOT NULL") }
scope :meaningfully_ordered, -> { nulls_first.order("table_column ASC") }
Model Load (12.0ms) SELECT "models".* FROM "models" ORDER BY table_column IS NULL DESC, table_column ASC LIMIT 1
Photo.order('collection_id DESC NULLS LAST')