1

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

我无法完全弄清楚如何做到这一点。有什么帮助吗?

4

3 回答 3

1

您可以为此使用 Arel 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 性能好,尽管您可能会发现它更容易理解。

于 2013-06-03T15:46:49.837 回答
0

这不是很令人满意(我真的很想知道如何只用 sql 来做),但我最终缓存了(使用 memcache)所有拥有有效许可证的学校的 id 然后只是测试学校的id 在该列表中。

就像我说的,我仍然希望看到一个纯 sql 解决方案,尤其是不涉及更多嵌套查询的解决方案。有一种感觉,我可以用 COALESCE() 做到这一点,但还没有完全弄清楚。

于 2013-06-04T08:39:10.900 回答
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})")
于 2013-06-03T15:05:46.057 回答