2012-03-14 83 views
1

我有三個模型Company,Deal和Slot。它們與Company has_many交易和Deal has_many槽位相關聯。如果所有交易都已過期,所有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 2012-03-14 19:18:21

+0

謝謝塔德曼.. – Vikram 2012-03-14 19:23:19

回答

1

,並具有比或SQL更高的優先級,以便您where實際上被解析如下:

(
     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。使用scoped而不是self也是一個好主意,如果其他範圍已經在使用,但如果使用類方法,則不必在意。

如果我們修復了一些括號您的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:基於在評論的討論,我覺得你這樣的事情之後是:

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 

污穢的底部這一點抓住具有的所有公司的ID十個或更多過期或已使用的槽,然後companies.id not in (...)將它們從最終結果集中排除。

+0

它總是很好地瞭解更多最佳實踐。但是我的問題還沒有解決。該查詢還提取了混合槽已過期(滿足上述條件)且未到期的公司。我很抱歉我在Mysql上非常糟糕。請看看這個日誌。 [鏈接](http://s1079.photobucket.com/albums/w518/vikramkohli87/?action=view¤t=Screenshotat2012-03-15022221.png) – Vikram 2012-03-14 21:00:54

+0

@Vikram:你真的想'slots.state = 1和插槽。那麼begin_at <=:time'?我不知道你的模式,所以我正在猜測。 – 2012-03-14 21:21:01

+0

當所有插槽銷售(狀態= 1)或插槽不再有效(slots.begin_at <=:時間)時,公司已過期。如果一家公司甚至有一個沒有過期或無效的插槽,該公司沒有過期。從上面的方案,它也返回公司只有1個時隙過期/無效10。我想要的是,它應該只返回公司只有當所有10個插槽過期/無效。 – Vikram 2012-03-15 03:53:14