views:

112

answers:

5

Hi,

I'm muddling my way through by far the most complex SQL query i've ever done, which is probably extremely simple for most of you (:

I have three tables, User, Skills and User_Skills. The fields of which should be fairly self explanatory.

I want to choose people who have one or more skills that meet my criteria.

I can select a user with the skill i'm after, but i'm not sure about the syntax for querying multiple skills.

I'd like to just use one query, so I'm trying to use GROUP_CONCAT

Here's my SQL:

SELECT User_id, first_name, last_name, county, GROUP_CONCAT(CAST(Skill_id AS CHAR))
FROM User LEFT JOIN User_Skills ON User.id = User_Skills.User_id
LEFT JOIN Skills ON User_Skills.Skill_id = Skills.id GROUP BY User_id


User_id  first_name  last_name  county        GROUP_CONCAT(CAST(Skill_id AS CHAR))
1000    Joe      Blow     West Yorkshire  8,6,1,9,7,3,5,10
1001    Fred      Bloggs     COUNTY1          5,8,2,7,9
1003    asdf      asdf1     Some County      10,8,2

How do I limit the search to only people who have skill 5 AND 9 ?

A: 

Why can't you just use an INNER JOIN inplace of the LEFT join, and issue a WHERE clause for the required skills?

SELECT User_id, first_name, last_name, county, GROUP_CONCAT(CAST(Skill_id AS CHAR)) 
FROM User 
INNER JOIN User_Skills ON User.id = User_Skills.User_id 
INNER JOIN Skills ON User_Skills.Skill_id = Skills.id GROUP BY User_id
WHERE skill_id = 5 OR skill_id=9
Jamiec
Thanks, but this won't work because I need users to have both skills, not one or the other.
Samuurai
+2  A: 
SELECT User_id, first_name, last_name, county, GROUP_CONCAT(CAST(Skill_id AS CHAR))
FROM User JOIN User_Skills ON User.id = User_Skills.User_id
JOIN Skills ON User_Skills.Skill_id IN (5,9)
GROUP BY User_id

If you want to prevent multiple skill records, then you may need a subquery

SELECT User_id, first_name, last_name, county,
(
    SELECT (GROUP_CONCAT(CAST(subSkill.Skill_id AS CHAR))
    FROM Skills as subSkill WHERE subSkill.skill_id = User_Skills.skill_id
    GROUP BY subSkill.skill_id
)
FROM User JOIN User_Skills ON User.id = User_Skills.User_id
JOIN Skills ON User_Skills.Skill_id IN (5,9)
GROUP BY User_id
Zoidberg
Never used the IN method before.. thanks for that, however it's showing this as results User_id first_name last_name county GROUP_CONCAT(CAST(Skill_id AS CHAR))1000 Joe Blow West Yorkshire 5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,7,8,9,9,9,9,9,9,9,9,...
Samuurai
There, I updated my answer, see if thats what you need and we'll go from there.
Zoidberg
Thanks again! It's not working properly because GROUP_CONCAT(CAST(Skill_id AS CHAR)) isn't in the field list. Perhaps because we're selecting it before it's been joined? I'm not sure.
Samuurai
Is there a reason you need it?
Zoidberg
Yeah, because it's throwing an error!
Samuurai
k, i will update my query when I have a moment to do so.
Zoidberg
A: 

Here's how (will give you users with skills 5 AND 9):

SELECT User_id, first_name, last_name, county FROM User INNER JOIN User_Skills ON User.id = User_Skills.User_id INNER JOIN Skills ON User_Skills.Skill_id = Skills.id WHERE Skills.Skill_id = 5 AND Skills.Skill_id = 9 GROUP BY User_id
Håvard S
Jamiec
A: 
SELECT  *
FROM    users u
WHERE   EXISTS
        (
        SELECT  NULL
        FROM    user_skills us
        WHERE   us.skill_id IN (5, 9)
                AND us.user_id = u.id
        LIMIT 1, 1
        )

This relies on the fact that (user_id, skill_id) is a PRIMARY KEY or a UNIQUE key (that is you cannot assign a skill to a user more than once).

Update:

To return all skills (comma separated):

SELECT  u.*,
        (
        SELECT  GROUP_CONCAT(skill_id)
        FROM    user_skills uso
        WHERE   uso.user_ud = u.user_id
        ) AS all_skills
FROM    users u
WHERE   EXISTS
        (
        SELECT  NULL
        FROM    user_skills us
        WHERE   us.skill_id IN (5, 9)
                AND us.user_id = u.id
        LIMIT 1, 1
        )
Quassnoi
Thanks for that! How can it be extended to show the skill_id as well?
Samuurai
Which `skill_id`? You can have many of them
Quassnoi
… and all of them match.
Quassnoi
I think I may have asked the question incorrectly.I want to return all the users which have specific skills, including ALL of the skills they possess.
Samuurai
A: 

You just need a

HAVING FIND_IN_SET('5', skills) AND FIND_IN_SET('9', skills)

after the group by (where "skills" is the name of the grouped column). So your query should now look like

SELECT User_id, first_name, last_name, county, GROUP_CONCAT(CAST(Skill_id AS CHAR)) as skills
FROM User LEFT JOIN User_Skills ON User.id = User_Skills.User_id
LEFT JOIN Skills ON User_Skills.Skill_id = Skills.id GROUP BY User_id
HAVING FIND_IN_SET('5', skills) AND FIND_IN_SET('9', skills)
mozillalives