如何高效地检查数据库在处理记录之前是否会返回结果。例如:Truck.where("id = ?", id).select('truck_no').first.truck_no
这个查询可能会返回卡车的信息,也可能不会,具体情况取决于卡车是否存在。那么,在处理这个请求时,如何确保页面不会崩溃呢?如果我使用循环遍历每个卡车并打印出其编号,我该如何在视图和控制器中处理呢?
如果记录不存在,我希望能够打印出一条消息,而不是什么都不显示。
如何高效地检查数据库在处理记录之前是否会返回结果。例如:Truck.where("id = ?", id).select('truck_no').first.truck_no
这个查询可能会返回卡车的信息,也可能不会,具体情况取决于卡车是否存在。那么,在处理这个请求时,如何确保页面不会崩溃呢?如果我使用循环遍历每个卡车并打印出其编号,我该如何在视图和控制器中处理呢?
如果记录不存在,我希望能够打印出一条消息,而不是什么都不显示。
if Truck.exists?(10)
# your truck exists in the database
else
# the truck doesn't exist
end
exists?
方法的优点在于不从数据库中选择记录(这意味着比选择记录更快)。
查询如下:
SELECT 1 FROM trucks where trucks.id = 10
exists?
不仅局限于对象的主键。你也可以传递这样的查询:Person.exists?(['name LIKE ?', "%#{query}%"])
。 - Arslan Ali以下是您可以检查的方法。
if Trucks.where(:id => current_truck.id).blank?
# no truck record for this id
else
# at least 1 record for this truck
end
where方法返回一个ActiveRecord::Relation对象(类似于包含where结果的数组),它可能为空,但永远不会为nil。
exists?
代替blank?
。这将执行一个查询来检查对象是否存在于数据库中,而不是检索整个对象。 - Grzegorz Łuszczekexists?
还是 blank?
。 - Maniblank?
可能会更糟,因为它会在检查是否存在任何记录之前返回 所有 记录,而 exists?
会向 SQL 语句添加 LIMIT 1
,以便最多只加载一条记录。请参见下面 @cristian 的答案。 - jaredsmithOP实际用例解决方案
最简单的解决方案是将您的数据库检查和数据检索合并为一个数据库查询,而不是分别进行数据库调用。您的示例代码接近并传达了您的意图,但在实际语法中有点偏差。
如果你只是简单地执行Truck.where("id = ?", id).select('truck_no').first.truck_no
,并且此记录不存在,则在调用truck_no
时它将抛出一个nil
错误,因为first
可能会检索到一个nil
记录,如果没有找到与您的条件匹配的记录。
这是因为您的查询将返回与您的条件匹配的对象数组,然后您在该数组上执行first
,如果未找到匹配记录,则是nil
。
一个相当干净的解决方案:
# Note: using Rails 4 / Ruby 2 syntax
first_truck = Truck.select(:truck_no).find_by(id) # => <Truck id: nil, truck_no: "123"> OR nil if no record matches criteria
if first_truck
truck_number = first_truck.truck_no
# do some processing...
else
# record does not exist with that criteria
end
我建议使用干净的语法,它本身就能“注释”,这样其他人就可以确切地知道你想要做什么。
如果你真的想走得更远,你可以为你的Truck
类添加一个方法,让它为你完成这个任务并传达你的意图:
# truck.rb model
class Truck < ActiveRecord::Base
def self.truck_number_if_exists(record_id)
record = Truck.select(:truck_no).find_by(record_id)
if record
record.truck_no
else
nil # explicit nil so other developers know exactly what's going on
end
end
end
if truck_number = Truck.truck_number_if_exists(id)
# do processing because record exists and you have the value
else
# no matching criteria
end
ActiveRecord.find_by
方法将检索与您的条件匹配的第一条记录,否则如果没有符合条件的记录,则返回 nil
。请注意,find_by
和 where
方法的顺序很重要;您必须在 Truck
模型上调用 select
。这是因为当您调用 where
方法时,实际上返回的是一个 ActiveRelation
对象,而这不是您在此处寻找的内容。
请参阅 ActiveRecord API 的 'find_by' 方法
使用 'exists?' 方法的通用解决方案
正如其他贡献者已经提到的那样,exists?
方法专门设计用于检查某些东西是否存在。它不返回值,只确认数据库中是否有与某些条件匹配的记录。ActiveRelation(Record?) where(...)
条件。email
属性的 User
模型,并且您需要检查电子邮件是否已经存在于数据库中:
User.exists?(email: "test@test.com")
使用 exists?
的好处是运行的 SQL 查询是:
SELECT 1 AS one FROM "users" WHERE "users"."email" = 'test@test.com' LIMIT 1
这比实际返回数据更有效率。truck_number_if_exists(id)
)都应该以问号结尾。 - Mani?
附加到布尔方法末尾;但是,在这种情况下,truck_number_if_exist(id)
不是布尔方法;如果存在值,则返回一个值,否则返回 nil。因为它不是布尔方法,所以不应在末尾使用 ?
。 - Dan LTruck.exists?(truck_id) # 返回 true 或 false
但如果卡车存在且您想要访问该卡车,则必须重新查找卡车,这将导致两个数据库查询。如果是这种情况,请使用:
@truck = Truck.find_by(id: truck_id) #returns nil or truck
@truck.nil? #returns true if no truck in db
@truck.present? #returns true if no truck in db
@truck_no = Truck.where("id = ?", id).pluck(:truck_no).first
如果没有找到记录,这将返回nil
,否则将返回第一个记录的truck_no
。
然后在您的视图中,您可以像这样简单地执行:
<%= @truck_no || "There are no truck numbers" %>
@truck_nos = Truck.where("id = ?", id).pluck(:truck_no)
<% truck_nos.each do |truck_no| %>
<%= truck_no %>
<% end %>
<%= "No truck numbers to iterate" if truck_nos.blank? %>
pluck
将从您的表中选择单个列。 - AgisRails有一个persisted?
方法,可用于您需要的方式
persisted?
有不同的目的。它用于检查一个对象是否已经被写入数据库。 - Arslan Alitruck.persisted?
会返回true,即使此后它已从数据库中删除。 - Matilda Smeds