[英]SQL LEFT JOIN value NOT in either join column
我怀疑这是一个相当普遍的情况,可能显示出我作为数据库开发人员的无能,但是无论如何这里都...
我有两个表: Profiles和HiddenProfiles ,并且HiddenProfiles表具有两个相关的外键: profile_id和hidden_profile_id ,用于存储Profiles表中的id 。
可以想象,一个用户可以隐藏另一个用户(其中,他的个人资料ID将为HiddenProfiles表中的profile_id ),或者他可以被另一个用户隐藏 (其中他的个人资料ID将被置于hidden_profile_id列中)。 同样,这是一个非常常见的情况。
期望的结果:
我想对“个人档案”和“ HiddenProfiles”表进行联接(或者说实话,无论哪种查询效率最高),以查找给定个人档案既不隐藏又不隐藏的所有个人档案。
在我的脑海中,我认为这将非常简单,但是我想到的迭代似乎一直遗漏了一半的问题。 最后,我得到的结果如下所示:
SELECT "profiles".* FROM "profiles"
LEFT JOIN hidden_profiles hp1 on hp1.profile_id = profiles.id and (hp1.hidden_profile_id = 1)
LEFT JOIN hidden_profiles hp2 on hp2.hidden_profile_id = profiles.id and (hp2.profile_id = 1)
WHERE (hp1.hidden_profile_id is null) AND (hp2.profile_id is null)
不要误会我的意思,这个“有效”的方法,但是在我的内心深处,我觉得应该有更好的方法。 如果实际上没有答案,我很乐意接受比我这个问题更聪明的人的回答。 :)
值得一提的是,Postgres DB上有两个RoR模型,因此欢迎为这些约束量身定制的解决方案。
Profile.not_hidden(given_profile)
因此,要获取个人资料,我需要执行以下操作:
Profile.not_hidden(given_profile)
再说一次,也许这很好,但是如果有更好的方法,我会很乐意接受。
如果您只想为单个配置文件获取此列表,则可以实现一个实例方法以在ActiveRecord中有效执行相同的查询。 我所做的唯一修改是对子查询的联合执行单个联接,并将条件应用于子查询。 这将减少需要加载到内存中的列,并希望更快(您需要对数据进行基准测试以确保):
class Profile < ActiveRecord::Base
def visible_profiles
Profile.joins("LEFT OUTER JOIN (
SELECT profile_id p_id FROM hidden_profiles WHERE hidden_profile_id = #{id}
UNION ALL
SELECT hidden_profile_id p_id FROM hidden_profiles WHERE profile_id = #{id}
) hp ON hp.p_id = profiles.id").where("hp.p_id IS NULL")
end
end
由于此方法返回ActiveRecord范围,因此可以根据需要链接其他条件:
Profile.find(1).visible_profiles.where("created_at > ?", Time.new(2015,1,1)).order(:name)
我个人从未喜欢过join = null方法。 我发现它与直觉相反。 您要进行联接,然后将结果限制为不匹配的记录。
我会更倾向于
SELECT id FROM profiles p
WHERE
NOT EXISTS
(SELECT * FROM hidden_profiles hp1
WHERE hp1.hidden_profile_id = 1 and hp1.profile_id = p.profile_id)
AND
NOT EXISTS (SELECT * FROM hidden_profiles hp2
WHERE hp2.hidden_profile_id = p.profile_id and hp2.profile_id = 1)
但是您将需要对它进行一些实际的解释,以确保最有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.