web-dev-qa-db-ja.com

Rails:whereステートメントでより大/小を使用する

200を超えるIDを持つすべてのユーザーを検索しようとしていますが、特定の構文に問題があります。

User.where(:id > 200) 

そして

User.where("? > 200", :id) 

両方とも失敗しました。

助言がありますか?

118
Adam Templeton

これを試して

User.where("id > ?", 200) 
239
RadBrad

これは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..と書くことができます。詳細情報 このブログ投稿内

97
Aaron

より良い使用法は、ユーザーモデルwhere(arel_table[:id].gt(id))にスコープを作成することです

21
Mihai

より直感的な記述が必要な場合は、 squeel というgemが存在します。これにより、次のような指示を記述できます。

User.where{id > 200}

「中括弧」文字{}およびidは単なるテキストであることに注意してください。

あなたがしなければならないのは、あなたのGemfileにsqueelを追加することです:

gem "squeel"

これにより、Rubyで複雑なSQLステートメントを作成するときの生活が大幅に楽になります。

5
Douglas

アレルはあなたの友達です。

User.where(User.arel_table [:id] .gt(200))

3
joegiralt

別の素晴らしい可能性は...

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)
1
Victor

私はしばしば日付フィールドでこの問題を抱えています(比較演算子は非常に一般的です)。

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回結合するときにエイリアスの衝突を回避できることです。

1
Brett Green