96
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ActiveRecordでは present? の代わりに exists? を使おう

Last updated at Posted at 2020-11-25

小ネタです。

条件に該当するレコードが存在するかを確認したい

ActiveRecordで条件に存在するレコードが存在するかを確認するのに、つい、

Model.where(conditions).present?

と書いてしまいがちですが、これはパフォーマンス上の問題が生じる可能性があります。

.present? は ActiveSupport によってモンキーパッチされたメソッドです。
Mode.where(conditions) のような ActiveRecord_Relation クラスのオブジェクトに .present? を適用するとどうなるか?

すると条件に該当するレコードを全てDBから取得して、(Rails上のモデルの)配列として評価することになります。
配列に要素が存在すれば true、 しなければ false ですね。

なぜこれはダメなのか

一見すると問題なさそうですし、実際結果自体は正しいのですが、条件に該当するレコードが1つでも存在することを確認できれば良いわけで、全件を取得する必要はありません。
100万レコードが条件に該当した場合、その100万レコードがDBからピックアップされ、その100万レコード分のデータがRails側に転送され、100万のモデルが作成された上で捨てられる。という壮大な無駄が発生します。結果、いつまでたっても結果が返ってこない。というパフォーマンス上の障害を引き起こしてしまうのです。

こういうのはレコード数の少ない開発環境では顕在化しずらく、レコード数の多い本番環境でいきなり顕在化してパニックになることがあります。

では、どうする?

.exists? メソッドを使いましょう。

Model.where(conditions).exists?

こちらを使うと、SQLが exists 式を使うものに代わります。
exists 式は条件に該当するレコードが1件でも存在すればDBがそこで探索を打ち切ってくれるので、無駄に全件を取ってくることはありません。
DBレベルで true または false で返してくれるのでオーバヘッドが最小で済みます。

present? の逆は?

.present? の逆である .blank? に対応するメソッドは .empty? になります。
.empty? を使えばSQL上は exists 式を使います。

ただ、条件に該当するレコードが存在しないことを確認するには、結局全件のチェックが必要になるので両者( .blank?.empty? )に速度的な差は少ないかもしれません。

96
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
96
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?