【问题标题】:Join many to many tables optionally可选择加入多对多表
【发布时间】:2021-01-23 22:57:02
【问题描述】:

我有以下表格

用户

user_id  name
101      Tony
102      Skyle
103      Kenne

兴趣

intrest_id intrest_name
201          Eating
202          Sleeping
203          Drinking

爱好

hobby_id  hobby_name
301         Smoking
302         Hiking
303         Browsing

用户兴趣

user_id  intrest_id
101      201
102      201
102      202
103      201
103      202
103      203

用户爱好

user_id  hobby_id
101      301
102      301
102      302
103      301
103      302
103      303

现在要找对EatingSleeping都有兴趣的用户id我写过

select u.user_id
from user u, intrest i, user_intrest ui
where u.user_id = ui.user_id 
and i.intrest_id = ui.intrest_id and i.intrest_name in ('Eating', 'Sleeping')
group by u.user_id
having count(i.intrest_name) = 2

输出

user_id
102
103

和上面一样,我也可以找到爱好SmokingHikingBrowsing的用户ID如下

select u.user_id
from user u, hobby h, user_hobby uh
where u.user_id = uh.user_id 
and h.hobby_id = uh.hobby_id and h.hobby_name in ('Smoking', 'Hiking', 'Browsing')
group by u.user_id
having count(i.intrest_name) = 3

输出

user_id
    103

现在我想选择混合这两种方式,如果只传递了兴趣,那么将找到具有这些兴趣的用户,或者如果只传递了爱好,则找到具有这些爱好的用户,或者如果兴趣和爱好都通过了,那么找到具有这些兴趣和爱好的用户

更新: 这是带有本机查询的 spring data rest api 的一部分,其中查询参数是可选的。下面是一个为兴趣寻找用户的工作示例。现在我想将其扩展为包括爱好,因此如果两者都在查找用户的请求中传递,则两者都将被使用

@RestResource(path = "getUserByIntrestsAndHobbies", rel = "getUserByIntrestsAndHobbies")
    @Query(value = "SELECT u FROM User u WHERE (COALESCE(:intrests, NULL) IS NOT NULL AND u.userId IN (SELECT u.userId " +
            "FROM User u, Intrest i, UserIntrest ui " +
            "WHERE u.userId = ui.userId AND i.intrestId = ui.intrestId " +
            "AND i.intrestName IN (:intrests) " +
            "GROUP BY u.userId " +
            "HAVING (:intrestsSize IS NULL OR :intrestsSize = count(i.intrestName))))"
    )
    Page<User> getUsersByIntrestsAndHobbies(@Param("intrests") List<String> intrests,
                                                                           @Param("intrestsSize") Long intrestsSize,
                                                                           @Param("hobbies") List<String> hobbies,
                                                                           @Param("hobbiesSize") Long hobbiesSize,
                                                                           Pageable pageable);

【问题讨论】:

  • 首先,停止使用隐式(逗号分隔)连接,因为它们已经过时多年了。而是使用显式连接。
  • 你说的只传递兴趣是什么意思?
  • 这个查询应该只能接受兴趣,如果爱好没有通过@GMB,则给有兴趣的用户
  • 对,但是值是如何传递到查询的?
  • 但是我们需要查看参数以什么形式到达...您的查询不包含参数 - 如果您要使用参数重新编写它,我们可以为您提供帮助。您已经向我们展示了字面值,但由于那不是现实生活,我们需要更多细节。

标签: sql sql-server join spring-data spring-data-rest


【解决方案1】:

好的,当您使用显式连接(而不是隐式、折旧、逗号分隔的连接)时,问题会变得更加清晰。然后您会看到您需要使用left 连接来允许在匹配时返回记录。

select u.[user_id]
from [user] u
left join user_intrest ui on ui.[user_id] = u.[user_id]
left join intrest i on i.intrest_id = ui.intrest_id and i.intrest_name in ('Eating', 'Sleeping')
left join user_hobby uh on uh.[user_id] = u.[user_id]
left join hobby h on h.hobby_id = uh.hobby_id and h.hobby_name in ('Smoking', 'Hiking', 'Browsing')
group by u.[user_id]
having count(distinct i.intrest_name) = 2 and count(distinct h.hobby_name) = 3;
  • 最好不要使用保留字,例如 useruser_id,因为您总是需要转义它们。

  • 除了id 列之外,不需要将表名添加到列名之前,例如hobby_name 应该只是 name - 你只是给自己更多的打字。

  • Interest 不是拼写 Intrest :)

  • 如果您为示例数据提供 DDL/DML,那么我们测试起来会容易得多。

【讨论】:

  • 感谢您的建议。这不是我刚刚模拟发布在这里的真实数据。此查询未根据数据给出预期结果。个别左连接正在为兴趣和爱好工作,但当混合时它不起作用。例如,用户 103 引用了兴趣和爱好,但查询返回为空
【解决方案2】:

我建议:

select i.userid
from (select ui.userid
      from user_interest ui join
           interest i
           on i.interest_id = ui.interest_id 
      where i.interest_name in ('Eating', 'Sleeping')
      group by ui.userid
      having count(*) = 2
     ) i join
     (select uh.userid
      from user_hobby uh join
           hobby h
           on uh.hobby_id = i.hobby_id 
      where h.hobby_name in ('Smoking', 'Hiking', 'Browsing')
      group by uh.userid
      having count(*) = 3
     ) h
     on i.userid = h.userid;

这将沿每个维度单独计数,因此计数是准确的。

【讨论】:

  • 如果只传入兴趣或爱好,那将不起作用 - 至少不是 OP 当前编写查询的方式。
猜你喜欢
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
相关资源
最近更新 更多