I have mapping table CandidatesSkills
which holds the mapping between candidate and the skills they possess. Then I have another table JobRequirements
that maps jobs and required skills for that jobs.
A candidate can apply to a job if he possesses ALL the required skills for that job. A candidate can have extra skills. Given CandiateID
I want to find all the jobs that candidate can apply.
I think this is Relational Division with Remainder in SQL. And there is an
Job to required skills mapping
based on the dataset, the query below should return JobID 2,3 and 5
Here my SQL (based on Peter Larsson (PESO) Solution for RDNR/RDWR)
DECLARE @CandidateID INT = 1
SELECT JobID
FROM
(
SELECT jr.JobID
,cnt=SUM(CASE WHEN jr.SkillID = c.SkillID THEN 1 ELSE 0 END)
,Items=COUNT(*)
FROM dbo.JobRequirements AS jr
CROSS JOIN dbo.CandidatesSkills AS c
WHERE c.CandidateID = @CandidateID
GROUP BY jr.JobID, jr.SkillID
) d
GROUP BY JobID
HAVING SUM(cnt) = MIN(Items)
AND MIN(cnt) >= 0;
However, query does not return anything. Trying to find what's wrong with my query
Here is the SQL Fiddle
CodePudding user response:
Something like:
DECLARE @CandidateID INT = 1;
with cj as
(
select cs.CandidateId,
jr.JobId,
count(*) over (partition by jr.JobId, cs.CandidateId) skillsPosessed,
(select count(*) from JobRequirements where JobId = jr.JobId) skillsRequired
from CandidatesSkills cs
join JobRequirements jr
on cs.SkillId = jr.SkillId
)
select distinct cj.CandidateId, cj.JobId
from cj
where cj.skillsPosessed = cj.skillsRequired
CodePudding user response:
In this case, you doing relational division with multiple divisors. In other words, you are dividing each set of JobRequirements
per each JobID
, by the CandidateSkills
of that candidate.
In this case, a LEFT JOIN
solution is much simpler
DECLARE @CandidateID INT = 1;
SELECT jr.JobID
,Skills = COUNT(c.SkillID)
,Requirements = COUNT(*)
FROM dbo.JobRequirements AS jr
LEFT JOIN dbo.CandidatesSkills AS c ON c.SkillID = jr.SkillID
AND c.CandidateID = @CandidateID
GROUP BY jr.JobID
HAVING COUNT(*) = COUNT(c.SkillID);
What this does is left-join the candidate's skills to the requirements. We then simply count up all the Requirements
for the JobID
, and ensure it is equal to the number of matches.
Another way to write this is
HAVING COUNT(CASE WHEN c.SkillID IS NULL THEN 1 END) = 0;
In other words: the number of non-matches should be zero.