SQL: как найти дополнение к набору с производной функцией / значением - PullRequest
1 голос
/ 11 июня 2009

Это меня поставило в тупик, так что я надеюсь, что тот, кто умнее меня, сможет мне помочь.

Я работаю над проектом 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

Однако обратное - найти всех пользователей, которые в данный момент отключены, - обманчиво сложно. Я закончил тем, что использовал следующее именованное определение области действия, хотя оно и хакерское:

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 ]

1 голос
/ 11 июня 2009

Помимо предположения @ Эрика, есть проблема (если вы не гарантировали от этого каким-либо иным способом, что вы нам не показываете), что у пользователя может не быть любой период времени - тогда внутреннее соединение не сможет включить этого пользователя, и он не будет отображаться как синхронизированный или как синхронизированный. Предполагая, что вы также хотите, чтобы эти пользователи были отключены, 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).

0 голосов
/ 11 июня 2009

В рельсах ответ Эрика Х должен выглядеть примерно так:

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

По крайней мере, я думаю, что это сработает ...

0 голосов
/ 11 июня 2009

при условии, что 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...