简体   繁体   中英

filter mysql inner join one to many results using and

i have two tables for example

users

----------------------------------
id  | name | blah
----------------------------------
1   | Samy | stackoverflow
2   | jhon | some thing
----------------------------------

skills

---------------------------------------
id | user_id | skill_title | level
---------------------------------------
1  | 1       | php         | good
2  | 1       | css         | excellent
3  | 1       | photoshop   | fair
4  | 2       | php         | good
---------------------------------------

and i run query like this

SELECT * FROM users 
INNER JOIN skills ON users.id = skills.user_id
WHERE ($skill_title[0] LIKE 'skills_title' AND 
       $skill_title[1] LIKE 'skills_title')

where $skill_title is an array what i need is to select user who have all this skills ie PHP,CSS if i did query like above it will never bring data because it compare every single record to all array element AND if i replaced and with Or it will work but it will no't bring user with all skills

any ideas ?

@joeshmo's answer is probably your best solution, but it might be worth giving this a try as well. It might be faster if you have a ton of users, but very few that have the skill you're looking for:

SELECT * FROM users
WHERE
    id IN (SELECT user_id FROM skills WHERE skill_title = '$skills[0]')
    AND id IN (SELECT user_id FROM skills WHERE skill_title = '$skills[1]')
    ...

And your PHP script might look like this:

$skills = array("php", "css", ... ); // replace ... with the skills you are looking for
$whereClause = array();
for ($i=0, $count=count($skills); $i < $count; $i++)
    $whereClause[] = "id IN (SELECT user_id FROM skills WHERE skill_title = '{$skills[$i]}')";

$query = "SELECT * FROM users". (count($whereClause) > 0 ? " WHERE ". implode(" AND ", $whereClause) : "");


Another possible solution would be to use multiple INNER JOIN s, as @joeshmo suggested. You might be able to make it a bit smaller and cleaner by doing this:

 SELECT * FROM users u INNER JOIN skills s1 ON u.id = s1.user_id AND s1.skill_title = '$skills[0]' INNER JOIN skills s2 ON u.id = s2.user_id AND s2.skill_title = '$skills[1]' ... 

So your PHP script might look something like this:

 $skills = array("php", "css", ... ); $joins = array(); for ($i=0, $count=count($skills); $i < $count; $i++) $joins[] = "INNER JOIN skills s{$i} ON u.id = s{$i}.user_id AND s{$i}.skill_title = '{$skills[$i]}'"; $query = "SELECT * FROM users u ". implode(" ", $joins); 

I would try both solutions to see which one performs better for your data set.

I think you want to join it again for each skill you're searching for.

SELECT * FROM users 
INNER JOIN skills as first_skill ON users.id = first_skill.user_id
INNER JOIN skills as second_skill ON users.id = second_skill.user_id
WHERE ($skill_title[0] LIKE first_skill.skill_title AND 
       $skill_title[1] LIKE second_skill.skill_title)

The problem with your query is that it is only joining the skills table once. Your query works like this:

-----------------------------------------------------
id  | name | blah           | skill_title | level
-----------------------------------------------------
1   | Samy | stackoverflow  | php         | good
1   | Samy | stackoverflow  | css         | excellent
1   | Samy | stackoverflow  | photoshop   | fair
2   | jhon | some thing     | php         | good
-----------------------------------------------------

No row has both css and php. My query works like this:

----------------------------------------------------------------------------------------------------------------
id  | name | blah           | first_skill.skill_title | first_skill.level | second_skill.skill_title | second_skill.level |
----------------------------------------------------------------------------------------------------------------
1   | Samy | stackoverflow  | php                     | good              | php                      | good               |
1   | Samy | stackoverflow  | php                     | good              | css                      | excellent          |
1   | Samy | stackoverflow  | php                     | good              | photoshop                | fair               |
... (there would be nine for Samy)
2   | jhon | some thing     | php                     | good              | php                      
----------------------------------------------------------------------------------------------------------------

The second row has both php and css . That's the one that would match.

This would be tedious for up to ten skills. You could do multiple queries instead.

$skills = Array("php","css", ..... );
$query = "  select u.id, u.name from users u left join skills s on u.id=s.user_id "
        ."  where s.skill_title in (\"".implode($skills,"\",\"")."\") "
        ."  group by s.user_id having count(s.id) = ".count($skills);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM