作成または初期化します。データベースとサーバーの負荷を最小限に抑える方法
概要
私は Ruby on Rails フレームワークで Ruby で書いています。仮定の状況が理解できません。 get_date 関数が約 1,000 万個の要素の配列を返したとします。各要素には、fk、name、detail のフィールドが含まれます。 同じフィールドを含むテーブルもあります。部分的に埋まっています。 fk - 一意のインデックス付きフィールド。 私の仕事は、テーブルにエントリが存在するかどうかを確認することです。記録がない場合は作成する必要があります。レコードが存在する場合は、名前フィールドと詳細フィールドが無関係になる可能性があるため、それらのフィールドを更新する必要があります。 データベースとサーバーへの負荷をできるだけ少なくするコードを書く必要があります
私は提案します:
get_data.each do |item|
field = MyField.find_or_initialize_by_fk(item['fk'])
field.update_attributes(
{
:name => item['name'],
:detail => item['detail']
}
)
field.save
end
または
get_data.each do |item|
field = MyField.find_or_create_by_fk(item['fk'])
field.update_attributes(
{
:name => item['name'],
:detail => item['detail']
}
)
end
これらともう一方のどちらが良いでしょうか?
解決策
最初のバージョンの方が優れています。これは、既存の可能性のあるレコードを検索するクエリと、既存のレコードを更新するか新しいレコードを作成するクエリの 2 つのクエリだけを実行するためです。
2 番目のバージョンは、既存のレコードが見つからなかった場合にデータベースに対して 3 つのリクエストを実行します。1 つはレコードの検索を試み、2 番目は新しいレコードを作成し、3 番目は新しく作成されたレコードを更新します。
ただし、代わりに、1 つのクエリで新しいレコードを挿入または既存のレコードを更新する upsert を使用することをお勧めします。
get_data.each do |item|
MyField.upsert(
{ fk: item['fk'], name: item['name'], detail: item['detail'] },
on_duplicate: 'name = EXCLUDED.name, detail = EXCLUDED.detail',
unique_by: :fk
)
end
たった 1 回のクエリで大量のレコードのバッチを挿入または更新できる upsert_all もあることに注意してください。 get_data の実装方法と使用方法に少し依存します。ただし、一般的な考え方は、レコードをバッチでロードし、1 つのクエリで各バッチを upsert_all することです。
もう 1 つのさらにパフォーマンスの高い方法は、SQL で 1 つのクエリを使用してこれを直接実行し、おそらく SQL の UPSERT コマンドを再度使用することです。それがどのように見えるかは、使用中のデータベース テーブルの正確なスキーマによって異なります。