可选地连接多对多表
Join many to many tables optionally
我有以下表格
用户
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_Intrest
user_id intrest_id
101 201
102 201
102 202
103 201
103 202
103 203
User_Hobby
user_id hobby_id
101 301
102 301
102 302
103 301
103 302
103 303
现在要查找既有兴趣Eating
又Sleeping
的用户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
同上我也可以找到爱好Smoking
,Hiking
,Browsing
的用户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
现在我想以一种可选的方式混合这两者,如果只传递兴趣,那么就会找到具有这些兴趣的用户,或者如果只传递兴趣爱好,那么就会找到具有这些爱好的用户,或者如果兴趣和爱好都是passed 然后找到具有这些兴趣爱好的用户
更新:
这是 spring 数据剩余 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);
好的,当您使用显式连接(而不是隐式、折旧的 comma-separated 连接)时,问题会变得更加清晰。然后您会看到您需要使用 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;
最好不要使用 user
或 user_id
等保留字,因为您总是需要对它们进行转义。
除了 id
列之外,不需要将 table 名称添加到列名称前面,例如hobby_name
应该只是 name
- 你只是给自己更多的输入。
Interest 不拼 Intrest :)
如果您提供DDL/DML作为示例数据,那么我们测试起来会容易得多。
我建议:
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;
这会分别沿每个维度进行计数,因此计数是准确的。
我有以下表格
用户
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_Intrest
user_id intrest_id
101 201
102 201
102 202
103 201
103 202
103 203
User_Hobby
user_id hobby_id
101 301
102 301
102 302
103 301
103 302
103 303
现在要查找既有兴趣Eating
又Sleeping
的用户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
同上我也可以找到爱好Smoking
,Hiking
,Browsing
的用户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
现在我想以一种可选的方式混合这两者,如果只传递兴趣,那么就会找到具有这些兴趣的用户,或者如果只传递兴趣爱好,那么就会找到具有这些爱好的用户,或者如果兴趣和爱好都是passed 然后找到具有这些兴趣爱好的用户
更新: 这是 spring 数据剩余 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);
好的,当您使用显式连接(而不是隐式、折旧的 comma-separated 连接)时,问题会变得更加清晰。然后您会看到您需要使用 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;
最好不要使用
user
或user_id
等保留字,因为您总是需要对它们进行转义。除了
id
列之外,不需要将 table 名称添加到列名称前面,例如hobby_name
应该只是name
- 你只是给自己更多的输入。Interest 不拼 Intrest :)
如果您提供DDL/DML作为示例数据,那么我们测试起来会容易得多。
我建议:
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;
这会分别沿每个维度进行计数,因此计数是准确的。