Rails join - 查找没有符合特定条件的连接的记录

时间:2013-06-03 14:51:20

标签: sql ruby-on-rails join ruby-on-rails-2

在我的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个有效许可证的学校。

我无法弄清楚如何做到这一点。有人帮忙吗?

3 个答案:

答案 0 :(得分:1)

您可以使用exists

来使用Arel
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()做到这一点,但还没有完全弄明白。