2
votes

I'm working in MS Access trying to iron out an SQL statement that works. On a form I have a combobox that displays a list of employees. I have a separate dialog form that allows the user to select multiple items in a listbox. Each item represents a certification. Each employee can have any number and combination of certifications. Ultimately I just want to update the RowSource property of the combobox to reflect the new filtered data by assigning a proper SQL statement.

If I want to filter the list on the combobox of employees, I use this SQL statement:

SELECT 
    Employees.Employee_ID, Employees.Last_Name, Employees.First_Name 
FROM 
    Employees 
INNER JOIN 
    Emp_Certs ON Employees.Employee_ID = Emp_Certs.Employee_ID 
WHERE 
    (((Employees.Active_Member) = Yes) 
      AND ((Emp_Certs.Employee_ID) = [Employees].[Employee_ID]) 
     AND ((Emp_Certs.Cert_ID) = 1)) 
ORDER BY 
    Employees.Last_Name;

If I run this query it works because I'm assigning only one value to Emp_Certs.Cert_ID. But when I add another like this:

SELECT 
    Employees.Employee_ID, Employees.Last_Name, Employees.First_Name 
FROM 
    Employees 
INNER JOIN 
    Emp_Certs ON Employees.Employee_ID = Emp_Certs.Employee_ID 
WHERE 
    (((Employees.Active_Member) = Yes) 
      AND ((Emp_Certs.Employee_ID) = [Employees].[Employee_ID]) 
      AND ((Emp_Certs.Cert_ID) = 1) 
      AND ((Emp_Certs.Cert_ID) = 4)) 
ORDER BY Employees.Last_Name;

I get an empty set. That's not what I expected. The table Emp_Certs clearly has several employees that have the combination of certifications 1 and 4. Could someone please explain how this is supposed to be written if I want to indicate more that one Cert_ID and have the employee record show up only once in the combobox. I don't need the employee records showing up multiple times in the combobox.

This might help:

enter image description here

4

4 Answers

1
votes

When you join tables, you basically query off a result set containing all the combinations of rows from those joined tables that your where clauses then operate off of. Since you are joining to the Emp_Certs table just once and linking only by Employee_ID, you are getting a result set that looks like this (only showing two columns):

Last_Name    Cert_ID
Jones        1
Jones        3
Jones        4
Smith        1
Smith        2

Your where clause then filters those rows, only accepting rows that have Cert_ID = 1 AND Cert_ID = 4, which is impossible so you should not get any rows.

I'm not sure if Access has limititations, but in SQL Server you could handle it in at least two ways:

1) Link to the table twice, joining for each of the certifications. Table alias 'a' joins to the Emp_Certs table where the Cert_ID is 1 and table alias 'b' joins to the Emp_Certs table where the Cert_ID is 4:

SELECT 
    Employees.Employee_ID, Employees.Last_Name, Employees.First_Name 
FROM 
    Employees 
INNER JOIN 
    Emp_Certs a ON Employees.Employee_ID = a.Employee_ID AND a.Cert_ID = 1
INNER JOIN 
    Emp_Certs b ON Employees.Employee_ID = b.Employee_ID AND b.Cert_ID = 4
WHERE 
    Employees.Active_Member = Yes
ORDER BY Employees.Last_Name;

This gives you a result set that looks like this (Smith doesn't show up because the join criteria doesn't allow any rows unless the employee can link to table a and b):

Last_Name    a.Cert_ID   b.Cert_ID
Jones        1           4

2) Use sub-selects in the where clause to filter the employee id on ids with those certifications (looks like Access 2010 supports it):

SELECT 
    Employees.Employee_ID, Employees.Last_Name, Employees.First_Name 
FROM 
    Employees 
WHERE 
    Active_Member = Yes
    AND Employee_ID in (SELECT Employee_ID FROM Emp_Certs WHERE Cert_ID = 1)
    AND Employee_ID in (SELECT Employee_ID FROM Emp_Certs WHERE Cert_ID = 4)
ORDER BY Employees.Last_Name;
0
votes

You should use OR operator in WHERE clause:

WHERE 
    (((Employees.Active_Member) = Yes) 
      AND ((Emp_Certs.Employee_ID) = [Employees].[Employee_ID]) 
      AND ( Emp_Certs.Cert_ID  = 1 OR
            Emp_Certs.Cert_ID  = 4 ) 
ORDER BY Employees.Last_Name;

because of Emp_Certs.Cert_ID = 1 and Emp_Certs.Cert_ID = 4 is always FALSE

Also the ((Emp_Certs.Employee_ID) = [Employees].[Employee_ID]) condition is redundant due to INNER JOIN condition

0
votes

Your query isn't working because Emp_Certs is probably only storing a single value. You can't check that the value is both 1 and 4. If your goal is to store multiple items into a single field, take a look here:

How to store array or multiple values in one column

You may be better off storing different certifications on different fields.

0
votes

What you are selecting now is for a row that contains both employee certs 1 and 4. You would want a query which would be implemented in access something like this...

SELECT DISTINCT Employees.Employee_ID, Employees.Last_Name, Employee.First_Name
FROM Emp_Certs AS a 
INNER JOIN (Emp_Certs INNER JOIN Employees ON Emp_Certs.Employee_Id = Employees.Employee_ID) ON a.Employee_ID = Employees.Employee_ID
WHERE (((Emp_Certs.CertId)=1) AND ((a.CertId)=4) AND ((Employees.Active_Member)=Yes))
ORDER BY Employee.Last;

This Edit has been tested as working in Access. It returns the results you requested. The key thing to remember when using query builder is that you have to join the Emp_Certs table twice.