简体   繁体   English

ActiveRecord的加入以及位置

[英]ActiveRecord joins and where

I have three models Company, Deal and Slot. 我有三个模型Company,Deal和Slot。 They are associated as Company has_many deals and Deal has_many slots. 它们与公司has_many交易和Deal has_many插槽关联。 All the A company can be expired if all of its deals are expired. 如果所有A公司的交易都已到期,则所有A公司都可以到期。 And a deal is expired when all of its slots are expired. 当交易的所有广告位都到期时,交易也会到期。

I have written a scope.. 我已经写了一个范围。

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
    )
  }

The above scope does not seem right to me from what I am trying to achieve. 从我要实现的目标来看,上述范围对我来说似乎不合适。 I need companies with all of its slots for all the deals are either in state 1 or the begin_at is less than :time making it expired. 我需要所有交易时段都处于状态1或begin_at小于:time使其过期的公司。

Thanks for having a look in advance. 感谢您提前查看。

AND has a higher precedence than OR in SQL so your where actually gets parsed like this: 并具有比或SQL让你一个更高的优先级where实际被解析如下:

(
        companies.spam = false
    and deals.deleted_at is null
    and deals.spam = false
    and slots.state = 1
)
or slots.begin_at <= :time

For example (trimmed a bit for brevity): 例如(为简洁起见,略有修饰):

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 |
+---+

Also, you might want to use a placeholder instead of the literal false in the SQL, that should make things easier if you want to switch databases (but of course, database portability is largely a myth so that's just a suggestion); 另外,您可能希望在SQL中使用占位符而不是文字上的false ,如果您要切换数据库,这应该会使事情变得更容易(但是,数据库可移植性在很大程度上是一个神话,所以这只是一个建议); you could also just use not in the SQL. 您也可以只在SQL中not使用。 Furthermore, using a class method is the preferred way to accept arguments for scopes . 此外, 使用类方法是接受scope参数的首选方法 Using scoped instead of self is also a good idea in case other scopes are already in play but if you use a class method, you don't have to care. 如果其他作用域已经在起作用,则使用scoped而不是self也是一个好主意,但是如果您使用类方法,则不必在意。

If we fix the grouping in your SQL with some parentheses, use a placeholder for false , and switch to a class method: 如果我们在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

You could also write it like this if you prefer little blobs of SQL rather than one big one: 如果您喜欢少量的SQL而不是一个大的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

This one also neatly sidesteps your "missing parentheses" problem. 这也巧妙地避免了您的“缺失括号”问题。


UPDATE : Based on the discussion in the comments, I think you're after something like this: 更新 :基于评论中的讨论,我认为您正在追求像这样的事情:

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

That bit of nastiness at the bottom grabs all the company IDs that have ten or more expired or used slots and then companies.id not in (...) excludes them from the final result set. 最底层的麻烦是获取具有十个或更多已过期或已使用插槽的所有公司ID,然后companies.id not in (...) company.id会将它们从最终结果集中排除。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM