Raylar: where deyiminden daha büyük / daha az kullanma


142

200'den büyük bir kimliğe sahip tüm Kullanıcıları bulmaya çalışıyorum, ancak belirli sözdiziminde bazı sorunlar yaşıyorum.

User.where(:id > 200) 

ve

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

her ikisi de başarısız oldu.

Baska öneri?

Yanıtlar:


276

Bunu dene

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

1
Ayrıca Ernie Miller
cpuguy83

7
?Satır içi kullanmak yerine, kullanmayı tercih etmek için herhangi bir neden var mı 200?
davetapley

20
otomatik olarak 200'den kaçar (kullanıcının değeri girmesi mümkün olsaydı, SQL enjeksiyon saldırıları olasılığını önler)
user1051849

124

Bunu sadece Rails 4'te test ettim, ancak wherebu davranışı elde etmek için karma içeren bir aralık kullanmanın ilginç bir yolu var .

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

SQL üretecek

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

Aynı şey, daha az ile yapılabilir -Float::INFINITY.

Ben sadece SO burada tarihlerle bunu yapmak isteyen soran benzer bir soru yayınladı .

>= vs >

İnsanları incelemek ve yorum konuşmalarını takip etmek zorunda kalmamak için burada öne çıkan özellikler vardır.

Yalnızca yukarıda yöntem oluşturur >=sorgu ve değildir bir >. Bu alternatifi ele almanın birçok yolu vardır.

Ayrık sayılar için

number_you_want + 1Kullanıcılarla ilgilendiğim id > 200ancak aslında aradığım yukarıdaki gibi bir strateji kullanabilirsiniz id >= 201. Bu, tek bir ilgi birimiyle arttırabileceğiniz tamsayılar ve sayılar için iyidir.

Sayıyı iyi adlandırılmış bir sabit haline getirdiyseniz, bu bir bakışta okunması ve anlaşılması en kolay olabilir.

Ters mantık

Biz gerçeği x > y == !(x <= y)kullanabilir ve zincir değil nerede kullanabilirsiniz.

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

hangi SQL üretir

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

Bu, okunması ve gerekçelendirilmesi için fazladan bir saniye sürer, ancak + 1stratejiyi kullanamayacağınız ayrık olmayan değerler veya sütunlar için işe yarayacaktır .

Arel masası

Eğer fantezi almak istiyorsanız kullanabilirsiniz Arel::Table.

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

SQL üretecek

"SELECT `users`.* FROM `users` WHERE (`users`.`id` > 200)"

Ayrıntılar aşağıdaki gibidir:

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`

Bu yaklaşım size tam olarak ilgilendiğiniz SQL'i sağlayacaktır, ancak pek çok kişi Arel tablosunu doğrudan kullanmaz ve dağınık ve / veya kafa karıştırıcı bulabilir. Siz ve ekibiniz sizin için en iyisini bilirsiniz.

Bonus

Rails 5'ten başlayarak bunu tarihlerle de yapabilirsiniz!

User.where(created_at: 3.days.ago..DateTime::Infinity.new)

SQL üretecek

SELECT `users`.* FROM `users` WHERE (`users`.`created_at` >= '2018-07-07 17:00:51')

Çifte Bonus

Ruby 2.6 yayınlandıktan sonra (25 Aralık 2018) yeni sonsuz aralık sözdizimini kullanabilirsiniz! Bunun yerine 201..Float::INFINITYsadece yazabilirsiniz 201... Bu blog yazısında daha fazla bilgi .


3
Bu neden meraktan, cevaptan daha üstün?
mecampbellsoup

5
Üstün yanıltıcıdır. Genel olarak, dizeler üzerinde karma sözdizimini kullanabiliyorsanız ARel sorgularınızla daha fazla esneklik elde edersiniz, bu nedenle birçoğu bu çözümü tercih eder. Projenize / ekibinize / kuruluşunuza bağlı olarak, koda bakarak birinin anlaması daha kolay olan ve kabul edilen cevabın hangisi olduğunu isteyebilirsiniz.
Aaron

2
Bunu temel whereeşleştiricileri kullanarak yapabileceğinize inanmıyorum . Çünkü basitlik için >bir kullanmanızı öneririz >= (number_you_want + 1). Gerçekten sadece bir >sorgu olduğundan emin olmak istiyorsanız ARel tablosuna erişebilirsiniz. Devralınan her sınıfın o sınıf için döndüren ActiveRecordbir arel_tablegetter yöntemi vardır Arel::Table. Tablodaki sütunlara []benzer yöntemle erişilir User.arel_table[:id]. Bu Arel::Attributes::Attribute, arayabileceğiniz gtve iletebileceğiniz bir döner 200. Bu daha sonra 'ya aktarılabilir where. örn User.where(User.arel_table[:id].gt(200)).
Aaron

2
@bluehallu bir örnek verebilir misiniz? Aşağıdakiler benim için çalışmıyor User.where(created_at: 3.days.ago..DateTime::Infinity.new).
Aaron

1
Ah! Muhtemelen Rails 5'de size faydalı davranışı veren yeni bir değişiklik. Rails 4.2.5'te (Ruby 2.2.2) bir sorgu alırsınız WHERE (users.created_at >= '2016-04-09 14:31:15' AND users.created_at < #<Date::Infinity:0x00>)(SO yorum biçimlendirmesi için atlanan tablo ve sütun adlarını geri alır).
Aaron

22

Daha iyi bir kullanım, kullanıcı modelinde bir kapsam oluşturmaktır where(arel_table[:id].gt(id))


6

Daha sezgisel bir yazı istiyorsanız, squeel adlı bir mücevher var, bu da talimatınızı şöyle yazmanıza izin verecek:

User.where{id > 200}

'Brace' karakterlerine {} ve idsadece bir metin olduğuna dikkat edin .

Tek yapmanız gereken Gemfile'ınıza squeel eklemek:

gem "squeel"

Bu, Ruby'de karmaşık SQL ifadeleri yazarken hayatınızı çok kolaylaştırabilir.


11
Squeel kullanmaktan kaçınmanızı tavsiye ederim. Uzun vadede bakımı zordur ve bazen tuhaf davranışlara sahiptir. Ayrıca bazı Aktif Kayıt sürümleri ile adamcağız
John Owen Şili

Birkaç yıldır squeel kullanıyorum ve hala mutluyum. Ama örneğin, netice (<> squeel) gibi başka bir ORM'yi denemeye değer, bu da ActiveRecord'un yerini almak için umut verici güzel özellikler var gibi görünüyor.
Douglas Douglas

5

Arel senin arkadaşın.

User.where (User.arel_table [: numarası] .gt (200))


4

Başka bir fantezi olasılığı ...

User.where("id > :id", id: 100)

Bu özellik, örneğin birden çok yerde değiştirmek istiyorsanız daha anlaşılır sorgular oluşturmanıza olanak tanır, örneğin ...

User.where("id > :id OR number > :number AND employee_id = :employee", id: 100, number: 102, employee: 1205)

Bu ?sorgu üzerinde bir sürü sahip daha fazla anlamı vardır ...

User.where("id > ? OR number > ? AND employee_id = ?", 100, 102, 1205)

3

Sık sık tarih alanlarıyla (karşılaştırma işleçlerinin çok yaygın olduğu) bu sorunu yaşıyorum.

Mihai'nin cevabı üzerinde daha ayrıntılı olarak durmak istiyorum, ki bu sağlam bir yaklaşım.

Modellere şu kapsamları ekleyebilirsiniz:

scope :updated_at_less_than, -> (date_param) { 
  where(arel_table[:updated_at].lt(date_param)) }

... ve daha sonra kumandanızda veya modelinizi kullandığınız her yerde:

result = MyModel.updated_at_less_than('01/01/2017')

... birleştirmelerle daha karmaşık bir örnek şöyle:

result = MyParentModel.joins(:my_model).
  merge(MyModel.updated_at_less_than('01/01/2017'))

Bu yaklaşımın büyük bir avantajı (a) sorgularınızı farklı kapsamlardan oluşturmanıza olanak tanır ve (b) arel_table sorgu oluşturma işleminin bu bölümünü ele alacağından aynı tabloya iki kez katıldığınızda takma ad çarpışmalarını önler.


1

Raylar 6.1+

Rails 6.1, wherekoşullardaki karşılaştırma operatörleri için yeni bir 'sözdizimi' ekledi , örneğin:

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

Böylece sorgunuz aşağıdaki gibi yeniden yazılabilir:

User.where('id >': 200) 

İşte halkla bir bağlantı Daha fazla örnek bulabilirsiniz.


-3

Daha kısa:

User.where("id > 200")

8
Bunun dinamik olması gerektiğini ve posterin parametreli sorguları ( where("id > ?", 200)sözdizimi) kullanarak SQL enjeksiyonundan kaçınmak istediğini varsayıyorum . Bu bunu başaramaz.
Matthew Hinea
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.