2012-03-14 3 views
1

私は3つのモデルCompany、Deal、Slotを持っています。彼らは、会社has_many dealとDeal has_many slotsとして関連付けられています。 A社のすべての取引が期限切れになると、すべてのA社は期限切れになる可能性があります。スロットの有効期限が切れた時点で取引が終了します。上記の範囲は、私が達成しようとしているものとは私にはないと思えるActiveRecordが加入しています。

scope :expired, 
    lambda { |within| 
    self.select(
     'DISTINCT companies.*' 
    ).latest(within).joins(
     :user =>{ :deals => :slots } 
    ).where(
     "companies.spam = false AND deals.deleted_at IS NULL 
     AND deals.spam = false AND slots.state = 1 
     OR slots.begin_at <= :time", 
     :time => Time.zone.now + SLOT_EXPIRY_MARGIN.minutes 
    ) 
    } 

私はスコープを書かれている

..私はすべての取引が州1にあるか、またはbegin_atが期限切れになっている時間よりも短いため、すべてのスロットを持つ企業が必要です。

ありがとうございます。

+1

私はそれを分割したので、めちゃくちゃ一行で読みにくかったです。 – tadman

+0

ありがとうTadman .. – Vikram

答えて

1

とあなたの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 scopesselfの代わりに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 (...)が最終結果セットから除外されます。

+0

ベストプラクティスをもっとよく知っているのは常に良いことです。しかし、私の問題はまだ解決されていません。クエリはまた、混合スロットが満了している(上記の条件を満たしている)ことが期限切れの企業をフェッチします。申し訳ありませんが、私はMysqlでひどく悪いです。このログを見てください。 [link](http://s1079.photobucket.com/albums/w518/vikramkohli87/?action=view¤t=Screenshotat2012-03-15022221.png) – Vikram

+0

@Vikram:実際には 'slots.state = 1とslotsが必要ですか? begin_at <=:time'それから?私はあなたのスキーマを知らないので、私はいくつかの推測をしています。 –

+0

すべてのスロットが販売されている(状態= 1)か、またはスロットが有効でなくなった(slots.begin_at <=:時間)場合、会社は期限切れになります。会社が期限切れでも無効でもない単一のスロットを持っている場合、会社は期限切れではありません。上記のスキームから、10スロットの有効期限切れ/無効のスロットが1つしかない会社も返されます。10スロットすべてが有効期限切れ/無効の場合にのみ、会社に返す必要があります。 – Vikram

関連する問題