简体   繁体   中英

Writing a difficult query for MS-Access (without MINUS operation, 3 table join)

I've come here for help with a complicated query. I've tried a lot of different things for this and not quite got the results that I want. A watered down version of my schema is as follows: The three tables in question are an Employees Table, an EmployeeTraining Table, and a RequiredTraining table. The employees table holds information of an employee, such as empID,name, address, email, positionWorked, etc etc. The EmployeeTraining table holds an empID, and TrainingName.The RequiredTraining table has a Position and TrainingName.

Employees: empID , Name, Position

EmployeeTraining: empID , trainingName

requiredTraining: Position , trainingName

Here's some sample data, just to further clarify:

Employees

              *EMPID~~Name~~Position~~
                  1      Ted     Accountant

                  2      Bob     Janitor

employeeTraining

                 **empID~~TrainingName**
                    1          Crunching Numbers

                    1          Microsoft Excel

                    1          Using an Abicus

                    2          Lemon Pledge 100

                    2          Scrubbing Toilets

requiredTraining

                  **position**~~**TrainingName**


                    Accountant    Crunching Numbers

                    Accountant    Microsoft Excel

                    Accountant    Using an Abicus

                    Accountant    TPS Reports

                    Janitor       Lemon Pledge 100

                    Janitor       Scrubbing Toilets

In english, and in the long run, I want to select an employee from a form and have a subform showing all of the training completed from that employee(SELECT * FROM Employees INNER JOIn EmployeeTraining on employees.empid = employeetraining.empID WHERE empID = me.empID)

I also want another subform that shows the training that an employee has NOT completed that is required for their current position. This is where I am struggling. I am not familiar or comfortable with using MS Access's query builder, but I have tried "find unmatched" queries as well as trying to write my own. This seems like a MINUS operation would work, but MS Access doesn't support MINUS. I have tried a LEFT JOIN with a null predicate as well as a NOT IN from the above query to a query of the requiredTraining table, but I haven't got the results I'm looking for.

For example, the result of the query I'm trying to write for this sample data would be:

for employeeID 1 (Ted the Accountant)

ted needs "TPS Reports" training

for employee 2 ( Bob the Janitor)

Bob has completed all of his training for his position.

An example of an attempted query I am trying is:

      SELECT employees.position,employeeTraining.empid,employeetraining.TrainingName          
      FROM EmployeeTraining 
      InNER JOIN Employees ON EmployeeTraining.empid = employees.empid
      WHERE empid = '1'
      LEFT JOIN
      SELECT TrainingName,Position FROM requiredTraining
      ON EmployeeTraining.Training = RequiredTraining.Training
      WHERE (((EmployeeTraining.Training is Null)))

Edit: This question has been answered. Thanks everyone for the help. Your queries, although each was different, all accomplished the goal.

The following query should return all employees with the training names that they need to complete:

select e.emp_id, rt.TrainingName
from employees e left outer join
     RequiredTraining rt
     on e.position = rt.position
where rt.TrainingName not in (select TrainingName from EmployeeTraining et where et.empId = e.EmpId)

The join in the "from" clause finds all the required trainings. The "where" clause then uses a correlated subquery to find the ones the employee has not completed.

you're very close actually, with your other query... you might try the NOT IN clause. For example, as you have said, you can get all the employees that have completed a particular training like so:

SELECT e.empID, e.name, e.Position, et.TrainingName
FROM Employees e
JOIN EmployeeTraining et ON e.empID = et.empID
WHERE empID = me.empID

What you want now, is all the employees that have NOT completed the training... you could just do a join on the trainingName table, making sure that the training type is not in the query you've already created, like so:

SELECT e.empID, e.name, e.Position, rt.TrainingName
FROM Employees e
JOIN requiredTraining rt ON e.Position = rt.Position
WHERE rt.TrainingName NOT IN
(
   SELECT et.TrainingName
   FROM Employees e
   JOIN EmployeeTraining et ON e.empID = et.empID
   WHERE empID = me.empID
)
  AND e.empID = me.empID

Note that, as mentioned above, you don't actually need to join on the employees table... you could just use the following:

SELECT e.empID, e.name, e.Position, rt.TrainingName
FROM Employees e
JOIN requiredTraining rt ON e.Position = rt.Position
WHERE rt.TrainingName NOT IN
(
   SELECT et.TrainingName
   FROM EmployeeTraining
   WHERE et.empID = me.empID
)
  AND e.empID = me.empID

The query below is created from two MS Access queries with the SQL cut and pasted into derived tables. The two queries select the employee with their current training (#1) and the employee with their required training (#2). It is not updateable, but is easy to create. You can show only the missing training by adding a line at the end:

WHERE et.TrainingName Is Null

To select a single employee, you can add a line:

WHERE rt.EMPID=1 AND et.TrainingName Is Null

However, you mention a subform, so I expect you will wish to use link child and master fields to limit by employee.

SQL

SELECT rt.empid,
       rt.position,
       rt.trainingname,
       et.trainingname AS IsTrained
FROM   (SELECT employees.empid,
               requiredtraining.position,
               requiredtraining.trainingname
        FROM   employees
               INNER JOIN requiredtraining
                       ON employees.position = requiredtraining.position) AS rt
       LEFT JOIN (SELECT employees.empid,
                         employees.position,
                         employeetraining.trainingname
                  FROM   employees
                         INNER JOIN employeetraining
                                 ON employees.empid = employeetraining.empid) AS
                 et
              ON ( rt.trainingname = et.trainingname )
                 AND ( rt.position = et.position )
                 AND ( rt.empid = et.empid ); 

The following query returns this result set based on your sample data as tested with Access 2007.

EMPID Position    TrainingName
1     Accountant  TPS Reports

SELECT
    sub.EMPID,
    sub.Position,
    sub.TrainingName
FROM
    (
        SELECT
            e.EMPID,
            e.Position,
            rt.TrainingName
        FROM
            Employees AS e
            INNER JOIN RequiredTraining AS rt
            ON e.Position = rt.position
    ) AS sub
    LEFT JOIN EmployeeTraining AS et
    ON
            sub.TrainingName = et.TrainingName
        AND sub.EMPID = et.empID
WHERE et.TrainingName Is Null;

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