Select From Multi-Value List with Where Clause
-
30-06-2021 - |
Question
I'm trying to select all Projects
which have Employees
who are AtWork
.
Projects:
ProjName | EmpOnProj
--------------------------
Alpha | 1, 2, 3
Beta | 1, 3
Employees:
EmpID | EmpName | AtWork
-------------------------------------
1 | John | TRUE
2 | Mark | FALSE
3 | Mary | TRUE
I need to output all projects which could currently be worked on; ie, I need to show Beta
because the employees working on Beta are at work.
Currently I cannot say "ALL EMPLOYEES MUST BE AT WORK" only the following:
SELECT ProjName FROM Projects INNER JOIN
Employees ON EmpOnProj.Value = EmpID
WHERE AtWork = true
GROUP BY ProjName
which returns both, as it sees one correct employee and displays the project.
Solution
I think I solved this one. Basically I'm saying 'show all projects except those where somebody is NOT at work'
http://sqlfiddle.com/#!3/36c48/2
SELECT DISTINCT
p_global.ProjName
FROM
Projects AS p_global
WHERE
p_global.ProjName NOT IN
(SELECT DISTINCT
p1.ProjName
FROM
Projects p1 INNER JOIN Employees AS e ON p1.EmpOnProj = e.EmpID
WHERE
e.AtWork = 0)
There may be a simpler solution but this works (or it looks like it anyway) :)
Edit: Modified to remove GROUP BY
s as suggested in comments.
OTHER TIPS
If can't really answer your question but this stuff can guide you through simplification and can help you solve you question. Currently your table is not in good format. Instead of having comma separated values, why not do it in rows instead? Like this,
Projects:
ProjName | EmpOnProj
--------------------------
Alpha | 1
Alpha | 2
Alpha | 3
Beta | 1
Beta | 3
In this way you can easily join both tables. Example
SELECT a.EmpID, a.EmpName,
iif (ISNULL(b.EmpOnProj), 'False', 'True') AtWork
FROM Employees a
LEFT JOIN Projects b
ON a.EmpID = b.EmpOnProj
WHERE b.ProjName = 'Beta'
Let's assume you move beyond the EmpOnProj column, which, as implemented in your example, violates first normal form, and replace it with an associative entity called ProjEmp, whose primary key is (ProjName, EmpID)
SELECT p.ProjName FROM Projects p
LEFT OUTER JOIN
(SELECT eop.ProjName FROM ProjEmp eop
INNER JOIN JOIN Employees e
ON e.EmpId = eop.EmpId
AND e.AtWork = FALSE
) AS empNotHere
ON empNotHere.ProjName = p.ProjName
WHERE empNotHere.ProjName IS NULL
;
I'm assuming I misunderstood your question since your SQL seems to contradict the schema you provided. But if your table is formatted the way you listed, you have to jump through hoops. Here's a solution that involves making a UDF, to avoid extremely complicated SQL.
Add this to a module:
Function WhoIsAtWork() As String
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("Select * from Employees where AtWork = true Order by EmpID")
Do While Not rs.EOF
WhoIsAtWork = WhoIsAtWork & rs!EmpID & ", "
rs.MoveNext
Loop
If Len(WhoIsAtWork) <> 0 Then
WhoIsAtWork = Left(WhoIsAtWork, Len(WhoIsAtWork) - 2)
End If
rs.Close
Set rs = Nothing
End Function
Then your SQL would be this:
SELECT ProjName
FROM Projects
WHERE Projects.EmpOnProj=WhoIsAtWork();