在我的Rails 2.2.2应用程序中,我有两个表/模型连接如下:
School
has_many :licenses, :as => :licensable
License
belongs_to :licensable, :polymorphic => true
#important fields for this question:
#start_date: datetime
#end_date: datetime
如果我想搜索所有拥有当前许可证的学校,这很简单:
licensed_schools = School.find(:all, :include => [:licenses], :conditions => ["licenses.start_date < ? and licenses.end_date > ?", Time.now, Time.now])
这可以找到所有在联接表中拥有有效许可证的学校。到目前为止一切都很好。
但是,如果我想找到所有没有有效许可证的学校,那就更难了(到目前为止):例如,如果我这样做
unlicensed_schools = = School.find(:all, :include => [:licenses], :conditions => ["licenses.id is null or licenses.start_date > ? or licenses.end_date < ?", Time.now, Time.now]
然后我找回任何学校a)根本没有任何许可证(罚款)或b)至少有一个无效许可证,包括拥有旧(无效)许可证和新(有效)许可证的学校。
换句话说,它将返回所有没有许可证或拥有一个或多个无效许可证的学校(无论他们是否拥有有效的许可证)。它应该归还所有拥有0个有效许可证的学校。
我无法弄清楚如何做到这一点。有人帮忙吗?
答案 0 :(得分:1)
您可以使用exists
licenses = License.arel_table
School.where(
License.where(
licenses[:school_id].eq(School.arel_table[:id]).
and(licenses[:start_date].gte(Time.now)).
and(licenses[:end_date].lte(Time.now))).exists)
或者,您可以构建自定义LEFT JOIN并检查NULL:
licenses = License.arel_table
schools = School.arel_table
custom_join = schools.join(licenses, Arel::Nodes::OuterJoin).
on(licenses[:school_id].eq(schools[:id].
and(licenses[:start_date].gte(Time.now)).
and(licenses[:end_date].lte(Time.now))).join_sources
School.joins(custom_join).where(licenses: {id: nil})
您也可以完全跳过Arel并手动构建LEFT JOIN:
School.joins("LEFT JOIN licenses ON licenses.school_id = schools.id AND '#{Time.now.to_s(:db)}' BETWEEN licenses.start_date AND licenses.end_date").
where(licenses: {id: nil})
LEFT JOIN的效果通常不如EXISTS,尽管您可能会发现它更容易理解。
答案 1 :(得分:0)
这个怎么样?
School.find_by_sql("Select * from schools where id not in (select distinct(lisensable_id) from licenses where licensable_type='school' and licenses.start_date > #{Time.now} and licenses.end_date < #{Time.now})")
答案 2 :(得分:0)
这不是很令人满意(我真的很想知道如何用sql做到这一点)但我最终缓存了(使用memcache)所有做的学校的ID有效的许可证,然后只是测试学校的ID是否在该列表中。
就像我说我仍然希望看到一个纯粹的SQL解决方案,特别是那个不涉及更多嵌套查询的解决方案。有一种感觉我可以用COALESCE()做到这一点,但还没有完全弄明白。