2009-06-10 9 views
1

Celui-ci m'a bloqué, alors j'espère que quelqu'un de plus intelligent que moi pourra m'aider.SQL: comment trouver un complément à un ensemble avec une fonction/valeur dérivée

Je travaille sur un projet de rails dans lequel j'ai un modèle de l'utilisateur qui a une association de clock_periods joint à lui, ayant la définition partielle suivante:

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 

La requête SQL pour récupérer tous les cadencé utilisateurs est trivial:

SELECT users.* FROM users INNER JOIN clock_periods ON clock_periods.user_id = users.id 
    WHERE clock_periods.clock_out_time IS NULL 

L'inverse cependant - trouver tous les utilisateurs qui sont actuellement cadencées sur - est trompeusement difficile. Je fini par utiliser la définition suivante de la portée du nom, bien que son hackish:

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

Ce qui me dérange à ce sujet est qu'il semble que il devrait y avoir une façon de le faire dans SQL sans avoir recours à des déclarations générant comme

SELECT users.* FROM users WHERE users.id NOT IN (1,3,5) 

Quelqu'un a-t-il une meilleure solution ou est-ce vraiment la seule façon de le gérer?

Répondre

0

En supposant que getdate() = la fonction dans votre implémentation SQL qui renvoie un datetime représentant maintenant.

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 
0

En rails, la réponse de Eric H devrait ressembler à:

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

Au moins, je pense que cela fonctionnerait ...

1

Outre @ suggestion d'Eric, il y a la question (à moins que vous est garanti contre cela d'une autre manière que vous ne nous montrez pas) qu'un utilisateur n'a peut-être pas aucune période d'horloge - alors la jointure interne n'inclurait pas cet utilisateur et il ne s'afficherait pas comme cadencé ou comme expulsé. En supposant que vous voulez aussi montrer les utilisateurs comme cadencé à, SQL doit être quelque chose comme:

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) 

(ce genre de chose est l'utilisation principale de jointures externes telles que LEFT JOIN).

+0

+1 - Bon point, j'ai pensé à cela mais j'ai oublié quand j'écrivais ma réponse. –

Questions connexes