web-dev-qa-db-ja.com

Railsデータベースにレコードが存在するかどうかを確認する

データベースがレコードを処理する前に返すかどうかを確認する最も効率的な方法は何ですか。例:Truck.where("id = ?", id).select('truck_no').first.truck_no

これは、トラックが存在する場合、トラックを返す場合と返さない場合があります。このリクエストを処理するときにページがクラッシュしないようにする最も効率的な方法は何ですか?ループを使用して各トラックを通過し、その番号を印刷するとしたら、ビューとコントローラーの両方でこれをどのように処理しますか?.

レコードが存在しない場合は、レコードが見つからないというメッセージを出力できるようにします。

44
Bojan

オブジェクトの存在を確認したい場合、なぜuseが存在しないのですか?

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のRailsドキュメント? で他の例を見つけることができます。

94
cristian

これを確認する方法を次に示します。

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になることはありません。

35
Shaunak

OPの実際のユースケースソリューション

最も簡単な解決策は、個別のDB呼び出しを行う代わりに、DBチェックとデータの取得を1つのDBクエリに結合することです。サンプルコードは近く、意図を伝えていますが、実際の構文では少しずれています。

単純にTruck.where("id = ?", id).select('truck_no').first.truck_noを実行し、このレコードが存在しない場合、nilfirstを取得する可能性があるため、_truck_no_を呼び出すと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
_

自分がやろうとしていることを他の人が正確に把握できるように、それ自体を「コメント」するクリーンな構文を使用することをお勧めします。

本当にもう1マイル進んで行きたい場合は、これを実行して意図を伝える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メソッドの順序が重要であることに注意してください。 selectモデルでTruckを呼び出す必要があります。これは、whereメソッドを呼び出すと、実際にはActiveRelationオブジェクトを返しているため、ここでは探しているものではありません。

'find_by'メソッドについてはActiveRecord APIを参照

「exists?」を使用した一般的なソリューションmethod

他の貢献者の一部がすでに言及したように、_exists?_メソッドは何かの存在をチェックするために特別に設計されています。値を返すのではなく、DBにいくつかの条件に一致するレコードがあることを確認するだけです。

あるデータの一意性または正確性を検証する必要がある場合に役立ちます。良い点は、ActiveRelation(Record?) where(...)基準を使用できることです。

たとえば、User属性を持つemailモデルがあり、dBに電子メールが既に存在するかどうかを確認する必要がある場合:

User.exists?(email: "[email protected]")

_exists?_を使用する利点は、SQLクエリの実行が

_SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1_

実際にデータを返すよりも効率的です。

DBから実際に条件付きでデータを取得する必要がある場合、これは使用する方法ではありません。ただし、単純なチェックではうまく機能し、構文は非常に明確であるため、他の開発者はあなたが何をしているかを正確に知ることができます。複数の開発者がいるプロジェクトでは、適切な構文を使用することが重要です。クリーンなコードを記述し、コードにそれ自体を「コメント」させます。

8
Dan L

あなたはただすることができます:

@truck_no = Truck.where("id = ?", id).pluck(:truck_no).first

レコードが見つからない場合はnilを返し、_truck_no of 最初のみレコード.

次に、あなたの意見では、次のようなことができます:

<%= @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? %>
2
Agis

レコードが存在するかどうかだけを確認したい場合。 @cristianの答え、つまり.

Truck.exists?(truck_id) # returns true or false

しかし、トラックが終了した場合、そのトラックにアクセスしたい場合は、2つのデータベースクエリにつながるトラックを再度見つける必要があります。これが事実である場合

@trcuk = Truck.find_by(id: truck_id) #returns nil or truck
@truck.nil? #returns true if no truck is db
@truck.present? #returns true if no truck is db
0
Imran