如何查找连接表中没有行的随机项?
在我的Rails 4应用程序中,我有一个Item模型和一个Flag模型。 项目has_many
标志。 标志belong_to
物品。 Flag具有item_id
, user_id
和reason
属性。 我需要一种方法来查找未标记的随机待处理项。 我正在使用enum
待处理状态。
我相信我可以找到一个标记为的随机待处理项目:
@pending_item = Item.pending.joins(:flags).order("RANDOM()").first
但是如何找到未标记的随机待处理项?
使用not exists
来获取没有Flag
的Item
:
@pending_item = Item.pending. where("not exists (select 1 from flags where flags.item_id = items.id)"). order("RANDOM()").first
附注: order("RANDOM()").first
如果有许多符合条件的Item
,则order("RANDOM()").first
效率不高。 对于大型表,这可能更有效:
unflagged_pending_items = Item.pending. where("not exists (select 1 from flags where flags.item_id = items.id)") @pending_item = unflagged_pending_items.offset(rand unflagged_pending_items.count).first
或者,如果第一个查询不是太慢而且每次都不需要不同的随机项,则可以暂时缓存结果。
我不知道,使用子选择是否更好。 我更喜欢使用ID和外键,我可以使用它。
# initialize my instance variable to have it in the view always @pending_item = nil # get an array with all flagged item ids flagged_item_ids = Flag.group(:item_id).pluck(:item_id) # search for an unflagged item, if there are item ids in the array only, # because the statement could return a flagged item with an empty array in the condition if flagged_item_ids > 0 @pending_item = Item.pending.where.not(id: flagged_item_ids).order("RANDOM()").first end @pending_item # => an unflagged random item or nil
请记住,数组flagged_item_ids
可能包含大量标记的项ID。 这会占用大量内存。
解决方案#1
Item.pending.joins("LEFT OUTER JOIN ( SELECT id, item_id FROM flags ) AS temp ON temp.item_id = items.id") .where('temp.id = null') .order("RANDOM()") .first
因此,如果我们使用LEFT OUTER JOIN
,如果该项没有被标记, temp.id
将等于null
。
解决方案#2
Item.pending.where("id NOT IN ( SELECT item_id FROM flags )") .order("RAMDOM()") .first
很明显,我们发现一个没有标记的项目。