Ruby' s。与侦测

时间:2016-09-07 06:24:08

标签: ruby-on-rails ruby activerecord

我正在寻找一种更快且使用更少服务器处理的方法。在我的申请中,我可以同时使用.where.detect

其中:

User.where(id: 1)
# User Load (0.5ms)

检测:

User.all.detect{ |u| u.id == 1 }
# User Load (0.7ms). Sometimes increases more than .where

我理解.detect returns the first item in the list for which the block returns TRUE但如果我有数千名用户,它与.where的比较如何?

为了清晰而编辑。

此示例中使用了

.where,因为我可能不会单独查询id。如果我有一个名为" name"?

的表格列怎么办?

3 个答案:

答案 0 :(得分:3)

在这个例子中

User.find(1)        # or
User.find_by(id: 1)

将是最快的解决方案。因为两个查询都告诉数据库只返回一个匹配id的记录。一旦数据库找到匹配的记录,它就不会进一步查看,但会立即返回一条记录。

尽管

User.where(id: 1)

将返回与条件匹配的对象数组。这意味着:在找到匹配的记录后,数据库将继续查找与查询匹配的其他记录,因此始终扫描整个数据库表。在这种情况下 - 由于id很可能是一个具有唯一值的列 - 它将返回一个只有一个实例的数组。

相反
User.all.detect { |u| u.id == 1 }

将从数据库加载所有用户。这将导致将数千个用户加载到内存中,构建ActiveRecord实例,迭代该数组,然后丢弃所有与条件不匹配的记录。与仅从数据库中加载匹配记录相比,这将非常缓慢。

数据库管理系统经过优化,可以运行选择查询,您可以通过设计有用的模式并添加适当的索引来提高其执行此操作的能力。从数据库加载的每条记录都需要转换为ActiveRecord的一个实例并消耗内存 - 这两个操作都不是免费的。因此,经验法则应该是:尽可能直接在数据库中而不是在Ruby中运行查询。

答案 1 :(得分:1)

NB 在这种特殊情况下应该使用ActiveRecord#find,请参阅@spickermann的答案。

User.where在数据库级别执行,返回一条记录。

User.all.detect会将所有记录返回给应用程序,然后才会在ruby级别上遍历

那就是说,一个必须使用where。前者可以抵抗大量记录,可能有数十亿,执行时间/内存消耗几乎相同(O(1)。)后者甚至可能在数十亿条记录中失败。

答案 2 :(得分:0)

以下是一般指南:

在查找唯一记录时,请使用.find(id)。搜索非ID字段时,可以使用.find_by_email(email).find_by_name(name)之类的类似方法(这些查找方法自动生成),只要只有一条具有该特定值的记录即可。

如果您的查询对于.find_by查询而言过于复杂,或者您需要使用排序,但仍可以确定只希望返回一条记录,请使用.where(...).limit(1)

检索多条记录时使用.where(...)

仅在无法避免的情况下使用.detect。 .detect的典型用例是在非ActiveRecord枚举上,或者当您有一组记录但无法在SQL中写入匹配条件时(例如,如果它涉及复杂的函数)。由于.detect最慢,因此请确保在调用.detect之前,您已使用SQL尽可能缩小查询范围。 .any?和其他enumerable methods的同上。仅仅因为它们可用于ActiveRecord对象并不意味着它们是一个好主意;)