SQL:如何使用派生函数/值查找集合的补集

时间:2009-06-10 22:58:46

标签: sql ruby-on-rails ruby

这个让我感到难过,所以我希望有比我更聪明的人可以帮助我。

我正在开发一个rails项目,其中我有一个User模型,它与clock_periods的关联加入了它,具有以下部分定义:

User
  has_many :clock_periods
    #clock_periods has the following properties:
    #clock_in_time:datetime
    #clock_out_time:datetime
  named_scope :clocked_in, :select => "users.*",
      :joins => :clock_periods, :conditions => 'clock_periods.clock_out_time IS NULL'
  def clocked_in?
    #default scope on clock periods sorts by date
    clock_periods.last.clock_out_time.nil?
  end

用于检索所有时钟用户的SQL查询非常简单:

SELECT users.* FROM users INNER JOIN clock_periods ON clock_periods.user_id = users.id 
  WHERE clock_periods.clock_out_time IS NULL
然而,反过来说 - 找到目前正在淘汰的所有用户 - 看起来很困难。我最终使用了以下命名的范围定义,尽管它是hackish:

named_scope :clocked_out, lambda{{
  :conditions => ["users.id not in (?)", clocked_in.map(&:id)+ [-1]]
}}

让我感到困扰的是,似乎应该有一种方法可以在SQL中执行此操作,而无需生成如

之类的语句
SELECT users.* FROM users WHERE users.id NOT IN (1,3,5)

任何人都有更好的方法,或者这真的是处理它的唯一方法吗?

3 个答案:

答案 0 :(得分:1)

除了@ Eric的建议之外,还有一个问题(除非你保证不会以某种其他方式反对它,你没有向我们展示)用户可能没有任何时钟周期 - 然后是内部加入将无法包含该用户,并且他不会显示为时钟输入或时钟输出。假设您还希望将这些用户显示为时钟,那么SQL应该类似于:

SELECT users.*
  FROM users 
  LEFT JOIN clock_periods ON clock_periods.user_id = users.id 
  WHERE (clock_periods.clock_user_id IS NULL) OR
        (getdate() BETWEEN clock_periods.clock_out_time AND
                           clock_periods.clock_in_time)

(这种事情是外部联接的主要用途,例如LEFT JOIN)。

答案 1 :(得分:0)

假设getdate()= SQL实现中的函数,它返回现在表示的日期时间。

SELECT users.* FROM users INNER JOIN clock_periods ON clock_periods.user_id = users.id 
  WHERE getdate() > clock_periods.clock_out_time and getdate() < clock_periods.clock_in_time

答案 2 :(得分:0)

在rails中,Eric H的回答应该类似于:

users = ClockPeriod.find(:all, :select => 'users.*', :include => :user,
  :conditions => ['? > clock_periods.clock_out_time AND ? < clock_periods.clock_in_time',
  Time.now, Time.now])

至少,我认为这样可行......