用户具有许多身份。
class User < ActiveRecord::Base
has_many :identities
end
class Identity < ActiveRecord::Base
belongs_to :user
end
身份具有一confirmed:boolean
列。我想查询所有只有一个身份的用户。还必须确认此身份为假。
我已经试过了
User.joins(:identities).group("users.id").having( 'count(user_id) = 1').where(identities: { confirmed: false })
但是,这将返回具有一个身份的用户,confirmed:false
但是如果确认他们是真实的,他们还可以具有其他身份。我只希望只有一个已确认身份的用户:false,并且没有已确认属性为true的其他身份。
我也尝试过这样做,但是显然它很慢,我正在寻找合适的SQL来在一个查询中执行此操作。
def self.new_users
users = User.joins(:identities).where(identities: { confirmed: false })
users.select { |user| user.identities.count == 1 }
end
如果已经回答了这个问题,请提前道歉,但我找不到类似的帖子。
一种解决方案是使用Rails嵌套查询
User.joins(:identities).where(id: Identity.select(:user_id).unconfirmed).group("users.id").having( 'count(user_id) = 1')
这是查询生成的SQL
SELECT "users".* FROM "users"
INNER JOIN "identities" ON "identities"."user_id" = "users"."id"
WHERE "users"."id" IN (SELECT "identities"."user_id" FROM "identities" WHERE "identities"."confirmed" = 'f')
GROUP BY users.id HAVING count(user_id) = 1
我仍然认为这不是最有效的方法。虽然我只能生成一个SQL查询(意味着仅一个网络调用db),但我仍然必须执行两次扫描:一次在USERS表上扫描,一次在IDENTITIES表上扫描。可以通过为identities.confirmed
列建立索引来优化此操作,但这仍然不能解决两次完整扫描的问题。
对于那些了解查询计划的人来说,它是:
QUERY PLAN
-------------------------------------------------------------------------------------------
HashAggregate (cost=32.96..33.09 rows=10 width=3149)
Filter: (count(identities.user_id) = 1)
-> Hash Semi Join (cost=21.59..32.91 rows=10 width=3149)
Hash Cond: (identities.user_id = identities_1.user_id)
-> Hash Join (cost=10.45..21.61 rows=20 width=3149)
Hash Cond: (identities.user_id = users.id)
-> Seq Scan on identities (cost=0.00..10.70 rows=70 width=4)
-> Hash (cost=10.20..10.20 rows=20 width=3145)
-> Seq Scan on users (cost=0.00..10.20 rows=20 width=3145)
-> Hash (cost=10.70..10.70 rows=35 width=4)
-> Seq Scan on identities identities_1 (cost=0.00..10.70 rows=35 width=4)
Filter: (NOT confirmed)
(12 rows)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句