[英]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.