I have below tables
User
user_id name 101 Tony 102 Skyle 103 Kenne
Intrest
intrest_id intrest_name 201 Eating 202 Sleeping 203 Drinking
Hobby
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
Now to find user ids who have both interests Eating and Sleeping I have written
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
Output
user_id 102 103
Same as above I also can find user ids with hobbies Smoking, Hiking, Browsing as below
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
Output
user_id
103
Now I want to mix these two optionally in a way that if only interests are passed then users with those interests will be found or if only hobbies are passed then users with those hobbies are found or if both interests and hobbies are passed then users with those interests as well as hobbies are found
Update: This is part of a spring data rest api with native query where query parameters are optional. Below is a working example for finding users for intrests. Now I want to expand it to include hobbies so both will be used if both are passed in the request to find users
@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);
Advertisement
Answer
OK, the problem becomes a lot clearer when you use explicit joins (instead of the implicit, depreciated, comma-separated joins). You then see that you need to use left joins to allow a record to be returned when there is match.
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;
Its best practice not to use reserved words such as
useroruser_idas you will always need to escape them.Aside from the
idcolumn, there is no need to prepend the table name to a column name e.g.hobby_nameshould just bename– you’re just giving yourself more typing as it stands.Interest is not spelt Intrest 🙂
If you provide DDL/DML for the sample data then its a lot easier for us to test.