如何避免这个NOT IN子句?

时间:2014-11-27 12:33:30

标签: sql ruby-on-rails ruby

我有三张桌子:

方案

|  users |         | teams_users |      | teams |
 --------           -------------        -------
|   id   |         |   user_id   |      |   id  |
                   |   team_id   | 
                   | begin_date  |
                   |  end_date   |

因此。如果将用户分配给团队,则会创建新的team_users注册表ID,并且end_date为空。

如果用户被拉出团队,end_date将填入当前日期。

为什么会这样?

因为需要在该团队中保留该用户的历史记录。

问题

当我创建/编辑团队时,我想提供该团队的可用用户列表。该清单应遵循以下规则:

  • 用户不能在当前团队中
  • 用户可以在其他团队中
  • 用户可以拥有team_user注册表,其中包含end_date!= null

到目前为止的解决方案

我正在使用rails 2.3.14(不要问!)。到目前为止我的解决方案是:

#team_id is the current team

users_on_this_team = TeamUser.all(
   :conditions => {:team_id => team_id, :end_date => nil}
).collect { |team_user| team_user.user_id }

User.all(:conditions => ["id NOT IN (?)", users_on_this_team]

哪个有效,但感觉很乱。我想找到一个更好的解决方案,也许是使用JOIN,但我被卡住了。

任何想法?

1 个答案:

答案 0 :(得分:0)

您可以使用arel gem,以便推进查询:

ids = TeamUser.select("DISTINCT teams_users.user_id")
               .where(team_id: team_id, end_date: nil)
User.where(User.arel_table[:id].not_in(ids))

但我猜,更好的方法是使用特定的范围:

class User
   belongs_to :team_user

   scope :outteam_users, ->{ joins(team_user).where(TeamUser.
            arel_table[:user_id].not_eq(User.arel_table[:id]) }
end

注意两个查询都不一致。