Sql Rails连接-查找没有与特定条件匹配的连接的记录

Sql Rails连接-查找没有与特定条件匹配的连接的记录,sql,ruby-on-rails,join,ruby-on-rails-2,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 如果我想搜索所有拥有当前许可证的学校,

在我的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])
这会在join表中找到所有具有有效许可证的学校。到目前为止还不错

然而,如果我想找到所有没有有效执照的学校,到目前为止就更难了:例如,如果我有

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

我不太明白怎么做。有人需要帮忙吗?

这个怎么样

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})")
这个怎么样

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

您可以使用Arel进行此操作,方法是:

或者,您可以构建自定义左联接并检查空值:

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并手动构建左连接:

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

左联接的性能通常比现有联接要差,尽管您可能会发现它更容易理解。

您可以使用Arel来实现此功能,使用EXISTS:

或者,您可以构建自定义左联接并检查空值:

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并手动构建左连接:

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

左连接的性能通常比现有连接要差,尽管您可能会发现它更容易理解。

这不是很令人满意,我真的很想知道如何使用sql来实现这一点,但我最终使用memcache缓存了所有具有有效许可证的学校的ID,然后只是测试学校的id是否在列表中


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

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


就像我说的,我仍然希望看到一个纯sql解决方案,特别是一个不涉及更多嵌套查询的解决方案。有一种感觉,我可以用COALESCE来实现它,但还没有完全弄明白。

我以前没有见过Arel,现在正在查看github页面。看起来很有趣。我以前没见过阿雷尔,现在正在看github页面。看起来很有趣。这将只返回没有许可证的学校。在本例中,我希望学校没有基于开始日期和结束日期的有效许可证:学校可能有多个许可证,其中一些可能有效,而另一些可能无效。将条件添加到内部查询中。编辑此选项将只返回没有许可证的学校。在本例中,我希望学校没有基于开始日期和结束日期的有效许可证:学校可能有多个许可证,其中一些可能有效,而另一些可能无效。将条件添加到内部查询中。Edited唯一满足您需求的纯SQL条件要么涉及左连接,要么在相关子查询上存在调用-我在上面的“Rails/Arel”形式中演示了这两种方法。很抱歉,COALESCE在这里帮不了你。谢谢你的输入@PinnyM。我决定不再在我的应用程序中使用Arel。它确实让我想起了Sequel,我曾在其他非rails ruby应用程序中使用它作为ActiveRecord的替代品。ActiveRecord使用Arel-under-the-hood来实现它的魔力。Sequel是另一种DSL,它也在引擎盖下使用Arel。如果您对它不满意,那么我建议您手工编写SQL并使用find_by_SQL。满足您需求的唯一纯SQL条件可能涉及左连接或相关子查询上的调用-我在上面的“Rails/Arel”形式中演示了这两种方法。很抱歉,COALESCE在这里帮不了你。谢谢你的输入@PinnyM。我决定不再在我的应用程序中使用Arel。它确实让我想起了Sequel,我曾在其他非rails ruby应用程序中使用它作为ActiveRecord的替代品。ActiveRecord使用Arel-under-the-hood来实现它的魔力。Sequel是另一种DSL,它也在引擎盖下使用Arel。如果您对它感到不舒服,那么我建议您手工编写SQL并使用find_by_SQL。