按指定顺序按 id 查找 ActiveRecord 对象的简洁方法
我想获取给定 id 数组的 ActiveRecord 对象数组。
我假设
Object.find([5,2,3])
会按顺序返回一个包含对象 5、对象 2、然后对象 3 的数组,但相反,我得到一个按对象 2、对象 3、然后对象 5 排序的数组。
ActiveRecord Base find method API 提到您不应该期望它按照提供的顺序(其他文档没有给出这一点)警告)。
以相同顺序查找 id 数组?<中给出了一种可能的解决方案/a>,但 order 选项似乎对 SQLite 无效。
我可以编写一些 ruby 代码来自己对对象进行排序(要么有点简单但缩放效果较差,要么缩放效果更好但更复杂),但是有更好的方法吗?
I want to obtain an array of ActiveRecord objects given an array of ids.
I assumed that
Object.find([5,2,3])
Would return an array with object 5, object 2, then object 3 in that order, but instead I get an array ordered as object 2, object 3 and then object 5.
The ActiveRecord Base find method API mentions that you shouldn't expect it in the order provided (other documentation doesn't give this warning).
One potential solution was given in Find by array of ids in the same order?, but the order option doesn't seem to be valid for SQLite.
I can write some ruby code to sort the objects myself (either somewhat simple and poorly scaling or better scaling and more complex), but is there A Better Way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
MySQL 和其他数据库并不是自己对事物进行排序,而是它们不对其进行排序。 当您调用
Model.find([5, 2, 3])
时,生成的 SQL 类似于:这不指定顺序,仅指定您想要返回的记录集。 事实证明,通常 MySQL 会按
'id'
顺序返回数据库行,但不能保证这一点。让数据库按保证顺序返回记录的唯一方法是添加 order 子句。 如果您的记录始终以特定顺序返回,那么您可以向数据库添加一个排序列并执行
Model.find([5, 2, 3], :order => 'sort_column'). 如果不是这种情况,则必须在代码中进行排序:
It's not that MySQL and other DBs sort things on their own, it's that they don't sort them. When you call
Model.find([5, 2, 3])
, the SQL generated is something like:This doesn't specify an order, just the set of records you want returned. It turns out that generally MySQL will return the database rows in
'id'
order, but there's no guarantee of this.The only way to get the database to return records in a guaranteed order is to add an order clause. If your records will always be returned in a particular order, then you can add a sort column to the db and do
Model.find([5, 2, 3], :order => 'sort_column')
. If this isn't the case, you'll have to do the sorting in code:根据我之前对 Jeroen van Dijk 的评论,您可以使用
each_with_object
更有效地在两行中完成此操作,这里是我使用的基准,
现在另一个
更新
您可以在大多数使用 order 和 case 语句中执行此操作,这里是您可以使用的类方法。
Based on my previous comment to Jeroen van Dijk you can do this more efficiently and in two lines using
each_with_object
For reference here is the benchmark i used
Now the other one
Update
You can do this in most using order and case statements, here is a class method you could use.
显然 mySQL 和其他数据库管理系统会自行排序。 我认为你可以绕过这个做法:
Apparently mySQL and other DB management system sort things on their own. I think that you can bypass that doing :
一个可移植的解决方案是在 ORDER BY 中使用 SQL CASE 语句。 您可以在 ORDER BY 中使用几乎任何表达式,并且 CASE 可以用作内联查找表。 例如,您要执行的 SQL 如下所示:
使用一些 Ruby 即可轻松生成:
上面假设您正在使用
ids
中的数字或其他一些安全值; 如果情况并非如此,那么您需要使用 < code>connection.quote 或 ActiveRecord SQL 清理方法,以正确引用您的ids
。然后使用
order
字符串作为您的排序条件:或者在现代世界中:
这有点冗长,但它应该与任何 SQL 数据库一样工作,并且隐藏丑陋并不难。
A portable solution would be to use an SQL CASE statement in your ORDER BY. You can use pretty much any expression in an ORDER BY and a CASE can be used as an inlined lookup table. For example, the SQL you're after would look like this:
That's pretty easy to generate with a bit of Ruby:
The above assumes that you're working with numbers or some other safe values in
ids
; if that's not the case then you'd want to useconnection.quote
or one of the ActiveRecord SQL sanitizer methods to properly quote yourids
.Then use the
order
string as your ordering condition:or in the modern world:
This is a bit verbose but it should work the same with any SQL database and it isn't that difficult to hide the ugliness.
正如我在此处回答的那样,我刚刚发布了一个 gem (order_as_specified),允许您像这样进行本机 SQL 排序:
刚刚测试,它可以在 SQLite 中运行。
As I answered here, I just released a gem (order_as_specified) that allows you to do native SQL ordering like this:
Just tested and it works in SQLite.
Justin Weiss 写了一篇
告诉数据库首选顺序并直接从数据库加载按该顺序排序的所有记录似乎是一个好方法。 来自他的示例 博客文章:
允许您编写:
Justin Weiss wrote a blog article about this problem just two days ago.
It seems to be a good approach to tell the database about the preferred order and load all records sorted in that order directly from the database. Example from his blog article:
That allows you to write:
这是一个高性能的(哈希查找,而不是像检测中那样的 O(n) 数组搜索!)单行,作为一种方法:
Here's a performant (hash-lookup, not O(n) array search as in detect!) one-liner, as a method:
在 Ruby 中执行此操作的另一种(可能更有效)方法:
Another (probably more efficient) way to do it in Ruby:
这是我能想到的最简单的事情:
Here's the simplest thing I could come up with:
这可能是最简单的方法,假设您没有太多要查找的对象,因为它需要为每个 id 访问数据库。
This is probably the easiest way, assuming you don't have too many objects to find, since it requires a trip to the database for each id.