Rails:whereステートメントでの大なり/小なりの使用


142

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

User.where(:id > 200) 

そして

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

両方とも失敗しました。

助言がありますか?

回答:


276

これを試して

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

1
Ernie MillerのSqueel gemもチェックしてください
cpuguy83

7
?インライン化するよりもを使用するほうがよい理由はあります200か?
davetapley 2014

20
200を自動的にエスケープします(ユーザーが値を入力できたため、SQLインジェクション攻撃の可能性を回避できました)
user1051849

124

私はこれをRails 4でテストしただけですが、whereハッシュで範囲を使用してこの動作を実現する興味深い方法があります。

User.where(id: 201..Float::INFINITY)

SQLを生成します

SELECT `users`.* FROM `users`  WHERE (`users`.`id` >= 201)

同じことを未満で行うことができます-Float::INFINITY

私は、SOの日付これを行うことについて尋ねる同様の質問を投稿しました。

>=>

人々が掘り下げてコメントの会話に従う必要がないようにするために、ここにハイライトがあります。

上記の方法は、唯一の生成>=クエリおよびません>。この代替案を処理するには多くの方法があります。

離散数の場合

number_you_want + 1上記のような戦略を使用できますが、私はユーザーに興味がありますがid > 200、実際に探していid >= 201ます。これは、関心のある単一の単位で増分できる整数および数値の場合は問題ありません。

数値を適切な名前の定数に抽出した場合、一目でこれを読んで理解するのが最も簡単な場合があります。

反転論理

x > y == !(x <= y)チェーンを使用しないという事実を使用できます。

User.where.not(id: -Float::INFINITY..200)

SQLを生成する

SELECT `users`.* FROM `users` WHERE (NOT (`users`.`id` <= 200))

これは、読み取りに約1秒かかり、その理由を説明しますが、+ 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`

このアプローチでは、興味のある正確な SQL が得られますが、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..。詳細については、このブログ投稿をご覧ください。


3
好奇心から、これはなぜ受け入れられた答えより優れているのですか?
mecampbellsoup 2015年

5
スーペリアは誤解を招くものです。一般に、文字列に対してハッシュ構文を使用できる場合、ARelクエリを使用するとより多くの柔軟性が得られます。そのため、多くの人がこのソリューションを好みます。プロジェクト/チーム/組織によっては、コードをちらっと見た人が理解しやすいものを必要とする場合があります。
アーロン

2
基本的なwhereマッチャーを使用してそれを行うことができるとは思わない。>私が使用することをお勧め>= (number_you_want + 1)簡単にするために。本当に>クエリにしたい場合は、ARelテーブルにアクセスできます。継承することを、すべてのクラスActiveRecordがあるarel_table返すゲッターメソッドArel::Table、そのクラスのために。テーブルの列には、などの[]メソッドを使用してアクセスしますUser.arel_table[:id]。これは、Arel::Attributes::Attribute呼び出しgtて渡すことができるを返します200。その後、これをに渡すことができますwhere。例えばUser.where(User.arel_table[:id].gt(200))
アーロン

2
@bluehallu例を提供できますか?次は私のために働いていませんUser.where(created_at: 3.days.ago..DateTime::Infinity.new)
アーロン

1
ああ!おそらく、Rails 5の新しい変更により、有益な動作が得られます。Rails 4.2.5(Ruby 2.2.2)では、WHERE (users.created_at >= '2016-04-09 14:31:15' AND users.created_at < #<Date::Infinity:0x00>)(SOコメントのフォーマットではテーブル名と列名をバックティックで省略して)クエリが返されます。
アーロン

22

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


6

より直感的な記述が必要な場合は、squeelと呼ばれる宝石があり、次のような指示を記述できます。

User.where{id > 200}

「中かっこ」文字{}とid単なるテキストであることに注意してください。

Gemfileにsqueelを追加するだけです。

gem "squeel"

これにより、Rubyで複雑なSQLステートメントを作成するときに、生活が大幅に緩和される可能性があります。


11
squeelの使用は避けることをお勧めします。長期的に維持することは困難であり、奇妙な行動をとることがあります。また、特定のActive Recordバージョンではバグがあります
John Owen Chile

私は何年かスクイールを使っていますが、それでも満足しています。しかし、たぶん、続編(<> squeel)などの別のORMを試してみる価値はあります。これは、ActiveRecordに代わる優れた機能であると思われます。
ダグラス

5

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

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


4

別の空想の可能性は...

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)

3

日付フィールドでこの問題がよく発生します(比較演算子が非常に一般的です)。

しっかりとしたアプローチだと私は信じているミハイの答えについてさらに詳しく説明します。

モデルに次のようなスコープを追加できます。

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

Rails 6.1以降

Rails 6.1は、次のようなwhere条件の比較演算子に新しい「構文」を追加しました。

Post.where('id >': 9)
Post.where('id >=': 9)
Post.where('id <': 3)
Post.where('id <=': 3)

したがって、クエリは次のように書き換えることができます。

User.where('id >': 200) 

これはより多くの例を見つけることができるPRへのリンクです。


-3

より短い:

User.where("id > 200")

8
これは動的である必要があり、投稿者はパラメータ化されたクエリ(where("id > ?", 200)構文)を使用してSQLインジェクションを回避したいと思います。これはそれを達成しません。
Matthew Hinea 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.