とあなたのwhere
が実際にこのように解析されますので、よりOR SQLで優先順位が高くなります。たとえば
(
companies.spam = false
and deals.deleted_at is null
and deals.spam = false
and slots.state = 1
)
or slots.begin_at <= :time
(簡潔にするためにビットをトリミング):また
mysql> select 1 = 2 and 3 = 4 or 5 = 5;
+---+
| 1 |
+---+
mysql> select (1 = 2 and 3 = 4) or 5 = 5;
+---+
| 1 |
+---+
mysql> select 1 = 2 and (3 = 4 or 5 = 5);
+---+
| 0 |
+---+
、あなたは可能性がありますSQLではfalse
というリテラルの代わりにプレースホルダを使用したい場合、データベースを切り替える場合は、作業を簡単にする必要があります(もちろん、データベースの移植性はほとんど神話です)。あなたはまた、SQLでnot
を使用することもできます。さらに、using a class method is the preferred way to accept arguments for scopes。 self
の代わりにscoped
を使用することは、他のスコープがすでに使用されている場合には便利ですが、クラスメソッドを使用する場合は気にする必要はありません。
我々はいくつかの括弧を使用してSQLでグループ化を修正する場合は、false
のためのプレースホルダを使用すると、クラスメソッドに切り替える:
def self.expired(within)
select('distinct companies.*').
latest(within).
joins(:user => { :deals => :slots }).
where(%q{
not companies.spam
and not deals.spam
and deals.deleted_at is null
and (slots.state = 1 or slots.begin_at <= :time)
}, :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes)
end
あなたはSQLの小さな塊を好む場合にも、このようにそれを書くことができます
def self.expired(within)
select('distinct companies.*').
latest(within).
joins(:user => { :deals => :slots }).
where('not companies.spam').
where('not deals.spam').
where('deals.deleted_at is null').
where('slots.state = 1 or slots.begin_at <= :time', :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes)
end
これはまた、「欠けているかっこ」問題をきれいに回避します。
UPDATE:下部の意地の悪さのビットを持っているすべての企業IDをつかむこと
def self.expired(within)
select('distinct companies.*').
latest(within).
joins(:user => :deals).
where('not companies.spam').
where('not deals.spam').
where('deals.deleted_at is null').
where(%q{
companies.id not in (
select company_id
from slots
where state = 1
and begin_at <= :time
group by company_id
having count(*) >= 10
)
}, :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes
end
:コメント欄での議論をもとに、私はあなたがこのような何かの後だと思います10個以上のスロットが期限切れになっているか、または使用されている場合は、companies.id not in (...)
が最終結果セットから除外されます。
私はそれを分割したので、めちゃくちゃ一行で読みにくかったです。 – tadman
ありがとうTadman .. – Vikram