200を超えるIDを持つすべてのユーザーを検索しようとしていますが、特定の構文に問題があります。
User.where(:id > 200)
そして
User.where("? > 200", :id)
両方とも失敗しました。
助言がありますか?
これを試して
User.where("id > ?", 200)
これはRails 4でしかテストしていませんが、where
ハッシュを持つ範囲を使用してこの動作を取得する興味深い方法があります。
User.where(id: 201..Float::INFINITY)
sQLを生成します
SELECT `users`.* FROM `users` WHERE (`users`.`id` >= 201)
-Float::INFINITY
を使用する場合よりも少ない時間で同じことができます。
日付でこれを行うことについて尋ねる同様の質問を投稿しました ここSO 。
>=
vs >
人々が掘り下げてコメントの会話に従うことを避けるために、ここがハイライトです。
上記のメソッドは、>=
クエリとnot>
のみを生成します。この代替を処理する方法はたくさんあります。
離散数の場合
上記のようなnumber_you_want + 1
戦略を使用できます。ここでは、id > 200
を持つユーザーに興味がありますが、実際にはid >= 201
を探します。これは、関心のある単一の単位で増分できる整数および数値には適しています。
適切な名前の定数に番号を抽出した場合、これは一目で読みやすく理解しやすいかもしれません。
逆論理
x > y == !(x <= y)
という事実を使用し、where not chainを使用できます。
User.where.not(id: -Float::INFINITY..200)
sQLを生成します
SELECT `users`.* FROM `users` WHERE (NOT (`users`.`id` <= 200))
読み取りと推論に余分な時間がかかりますが、+ 1
戦略を使用できない非離散値または列に対しては機能します。
Arelテーブル
おしゃれにしたい場合は、Arel::Table
を使用できます。
User.where(User.arel_table[:id].gt(200))
sQLを生成します
"SELECT `users`.* FROM `users` WHERE (`users`.`id` > 200)"
詳細は次のとおりです。
User.arel_table #=> an Arel::Table instance for the User model / users table
User.arel_table[:id] #=> an Arel::Attributes::Attribute for the id column
User.arel_table[:id].gt(200) #=> an Arel::Nodes::GreaterThan which can be passed to `where`
このアプローチにより、興味のあるexactSQLが得られますが、Arelテーブルを直接使用する人は多くなく、面倒でわかりにくいものもあります。あなたとあなたのチームはあなたに最適なものを知っています。
Rails 5以降では、日付を使用してこれを行うこともできます。
User.where(created_at: 3.days.ago..DateTime::Infinity.new)
sQLを生成します
SELECT `users`.* FROM `users` WHERE (`users`.`created_at` >= '2018-07-07 17:00:51')
Ruby 2.6がリリースされると(2018年12月25日)、新しい無限範囲構文を使用できるようになります! 201..Float::INFINITY
の代わりに、単に201..
と書くことができます。詳細情報 このブログ投稿内 。
より良い使用法は、ユーザーモデルwhere(arel_table[:id].gt(id))
にスコープを作成することです
より直感的な記述が必要な場合は、 squeel というgemが存在します。これにより、次のような指示を記述できます。
User.where{id > 200}
「中括弧」文字{}およびid
は単なるテキストであることに注意してください。
あなたがしなければならないのは、あなたのGemfileにsqueelを追加することです:
gem "squeel"
これにより、Rubyで複雑なSQLステートメントを作成するときの生活が大幅に楽になります。
アレルはあなたの友達です。
User.where(User.arel_table [:id] .gt(200))
別の素晴らしい可能性は...
User.where("id > :id", id: 100)
この機能を使用すると、たとえば、複数の場所で置換する場合、よりわかりやすいクエリを作成できます。
User.where("id > :id OR number > :number AND employee_id = :employee", id: 100, number: 102, employee: 1205)
これは、クエリに?
をたくさん持つよりも意味があります...
User.where("id > ? OR number > ? AND employee_id = ?", 100, 102, 1205)
私はしばしば日付フィールドでこの問題を抱えています(比較演算子は非常に一般的です)。
Mihaiの答えをさらに詳しく述べると、それは堅実なアプローチだと思います。
モデルには、次のようなスコープを追加できます。
scope :updated_at_less_than, -> (date_param) {
where(arel_table[:updated_at].lt(date_param)) }
...そして、コントローラ内、またはモデルを使用している場所:
result = MyModel.updated_at_less_than('01/01/2017')
...結合を使用したより複雑な例は次のようになります。
result = MyParentModel.joins(:my_model).
merge(MyModel.updated_at_less_than('01/01/2017'))
このアプローチの大きな利点は、(a)異なるスコープからクエリを作成できること、および(b)arel_tableがクエリ生成のその部分を処理するため、同じテーブルに2回結合するときにエイリアスの衝突を回避できることです。