This should really be done as a 'set' rather than a loop. Imagine if you had a million people on here - it would need to do lots and lots of loops. Instead, the strength of databases is their ability to do all the functioning on all the rows at once.
As such, I've focussed on the set-based approach as an answer, and how to get from the "loop" approach to "set-based" approach. If you really want to do a WHILE loop, search Stack Overflow for 'WHILE LOOP SQL' or similar.
In terms of thinking and planning, there's nothing wrong to start with loops (indeed, for many people, thinking about the looping process is more natural). However, before coding it in SQL, try to convert the 'loop' into a 'set-based' answer. One way is to replace the approach "for each person, do this" to "for everyone, do this". Then you have to just work out how to get there.
You're half-way there with your answer - just a few more steps will get it done.
Below is an example of how you could approach this.
Also, as you haven't given us data structures etc - just some names - I made my own data structures for clarity. Also, for most of the answer, I have just reported Employee_ID, Job_ID etc rather than names - you can get those easily with referencing the tables though.
I've separated statements out for example purposes - but (as demonstrated at the end) you will be able to collapse/combine a lot of these into a single statement that gives you your answer.
PS I have assumed you meant 'employee' rather than 'employer' (i.e., you're talking about a person, not the company).
Data setup
-- Tables holding base data about employees, jobs and courses.
-- Note that an employee can only have one job in this model.
CREATE TABLE #Employees (Employee_ID int PRIMARY KEY, Employee_Name nvarchar(100), Job_ID int)
CREATE TABLE #Jobs (Job_ID int PRIMARY KEY, Job_Name nvarchar(100))
CREATE TABLE #Courses (Course_ID int PRIMARY KEY, Course_Name nvarchar(100))
-- Tables holding links between these
-- - the courses already completed by employees, and
-- - the courses required for each job
CREATE TABLE #Employee_Course (Employee_ID int, Course_ID int, PRIMARY KEY(Employee_ID, Course_ID))
CREATE TABLE #Job_Course (Job_ID int, Course_ID int, PRIMARY KEY(Job_ID, Course_ID))
-- Note I haven't set up data or other things like Foreign Keys etc - to keep this as focussed as possible
Now, for processing.
Your approach was to first work out (for a given job) who the relevant employees were. From there, you checked each employee to see if they had all the courses needed.
We'll change that slightly - instead of checking each employee, we will check all employees at once.
-- Step 1 - Identify employees in job
CREATE TABLE #Emps_in_job (Employee_ID int)
INSERT INTO #Emps_in_job (Employee_ID)
SELECT E.Employee_ID
FROM #Employees E
WHERE E.Job_ID = @Job_ID
SELECT * FROM #Emps_in_job ORDER BY Employee_ID
-- Step 2 - For all employees in that list, find the courses they need (based on their job)
-- Note this hasn't yet checked whether they have completed that course or not.
CREATE TABLE #Emp_Course_Requirements (Employee_ID int, Course_ID int)
INSERT INTO #Emp_Course_Requirements (Employee_ID, Course_ID)
SELECT E.Employee_ID, JC.Course_ID
FROM #Employees E
INNER JOIN #Job_Course JC ON E.Job_ID = JC.Job_ID
SELECT * FROM #Emp_Course_Requirements
-- Step 3 - Find out which courses are missing and report on them
-- There are three options here - all should work (select whichever you prefer)
-- Option 1 (using EXCEPT clause - easy to read but not frequently used)
SELECT ECR.Employee_ID, ECR.Course_ID
FROM #Emp_Course_Requirements ECR
EXCEPT
SELECT EC.Employee_ID, EC.Course_ID
FROM #Employee_Course EC
-- Option 2 (using LEFT OUTER JOIN, and a NULL result in that joined table indicating they don't have it)
SELECT ECR.Employee_ID, ECR.Course_ID
FROM #Emp_Course_Requirements ECR
LEFT OUTER JOIN #Employee_Course EC
ON ECR.Course_ID = EC.Course_ID
AND ECR.Employee_ID = EC.Employee_ID
WHERE EC.Employee_ID IS NULL
-- Option 3 (Delete completed employee/courses from the list. Any remaining must still be required)
DELETE FROM ECR
FROM #Emp_Course_Requirements ECR
INNER JOIN #Employee_Course EC
ON ECR.Course_ID = EC.Course_ID
AND ECR.Employee_ID = EC.Employee_ID
SELECT *
FROM #Emp_Course_Requirements ECR
However, even in the above there is extra processing. In the above we create temporary tables, fill them, and use them in the next step only. Instead, just put the original query into the second query (and so on) skipping the need to make a temporary table.
The final result could look like this
CREATE PROCEDURE #JobCourseCheck
@Job_ID int
AS
BEGIN
SET NOCOUNT ON;
SELECT E.Employee_Name, C.Course_Name
FROM #Employees E
INNER JOIN #Job_Course JC ON E.Job_ID = JC.Job_ID
INNER JOIN #Courses C ON JC.Course_ID = C.Course_ID
LEFT OUTER JOIN #Employee_Course EC
ON E.Employee_ID = EC.Employee_ID
AND JC.Course_ID = EC.Course_ID
WHERE E.Job_ID = @Job_ID
AND EC.Employee_ID IS NULL
END
JobName = @Functie
, however, there is no parameter (or variable)@Functie
defined in your procedure. This, however, feels like an XY Problem. Looping (in this case aWHILE
) is one of the last things you should be doing in SQL. SQL is a set based language and you should be using set based solutions. – Larnu