Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select Distinct on Inner Join Query filtering by multiple values on a single column?

So, i have to say, SQL is by far my weakest side as a developer. Perhaps what i'm trying to accomplish is pretty easy. I have something like this (It's not the real model, but in order to make it simple to understand without wasting too much time explaining it, i've come up with an example that mimics exactly the table relations i have to use). On one hand, a table, let's call it, "Users". Columns such as a primary key "UserId", "UserName", and so on.

Next, another Table, "Licenses". Relates 1:N with user, each license belongs to a single user, a user may have multiple licenses. So, we'd have a PK IdLicense, a FK IdUser, and so on.

Next, a table called "Equipments". here we have defined different equipments. IdEquipment, EquipmentName. Not much more to say about it.

Lastly, a table that creates a N:M relation between "Licenses" and "Equipments". when a user defines a license, they can specify the different equipments that licence allows them to manipulate. So, a license can relate to several equipments, and a piece of equipment can relate to multiple licenses.

The real deal comes here. I've been ordered to implement some kind of search system that allows you to get users based on the equipment they are qualified to handle.It's possible to make a multiple selection, so that you search for users that are qualified to handle equipment "A", "B" and "C". For example, we have 2 users, James and, Dave. James has 2 licenses, Dave has one. James is qualified to use machines "A" and "D" on his first license, "B" and "C" on the second one. Dave is qualififed to handle equipment type "B" and "C" on his only license. If someone tries to search for users being able to handle equipment "A","B" AND "C", only James would be the returning record.

So far i assumed i'd have to do something like

SELECT DISTINCT IdUser
FROM  Users
INNER JOIN Licenses
   ON Licenses.IdUser = Users.idUser
INNER JOIN LicensesEquipments
   ON LicensesEquipments.IdLicense = Licenses.IdLicense
INNER JOIN Equipment
   ON Equipment.IdEquipment = LicensesEquipments.IdEquipment

WHERE Equipment.IdEquipment = ??

How can i filter this on the all 3 different idEquipments for "A" "B" and "C".? Obviously, i can't make WHERE Equipment.IdEquipment = 1 and Equipment.IdEquipment = 2 since a cell can't be equal to 2 different values. Neither i can do WHERE Equipment.IdEquipment = 1 or Equipment.IdEquipment = 2 since using an OR would mean that any user holding at least ONE of the possibilities would be considered a valid result, and i'm looking for one that has ALL 3 different equipments on this case.

At first I assumed I'd have to create multiple inner joins with the table equipment, using alias, doing it as many times as filters on equipment i may have, and using each alias with a different filter. But then my code supervisor came by and told me to forget about it, since he said that would render too costly as more and more filters where added (though he gave me no better alternative...)

like image 981
Hobbes Avatar asked Oct 07 '22 03:10

Hobbes


1 Answers

SELECT Users.idUser
FROM Users
INNER JOIN Licenses ON Licenses.IdUser = Users.idUser
INNER JOIN LicensesEquipments ON LicensesEquipments.IdLicense = Licenses.IdLicense
INNER JOIN Equipment ON Equipment.IdEquipment = LicensesEquipments.IdEquipment
WHERE Equipment.IdEquipment IN ('A', 'B', 'C')
GROUP BY IdUser
HAVING COUNT(DISTINCT Equipment.IdEquipment) = 3 <--must be # of unique items in IN clause

If you really just need the user ID, you can drop the Users and Licenses tables from your query:

SELECT Licenses.IdUser
FROM Licenses 
INNER JOIN LicensesEquipments ON LicensesEquipments.IdLicense = Licenses.IdLicense
WHERE LicensesEquipments.IdEquipment in ('A', 'B', 'C')
GROUP BY Licenses.IdUser
HAVING COUNT(DISTINCT LicensesEquipments.IdEquipment) = 3

Also, some aliases makes it easier to read:

SELECT l.IdUser
FROM Licenses l
INNER JOIN LicensesEquipments le ON le.IdLicense = l.IdLicense
WHERE le.IdEquipment in ('A', 'B', 'C')
GROUP BY l.IdUser
HAVING COUNT(DISTINCT le.IdEquipment) = 3
like image 77
D'Arcy Rittich Avatar answered Oct 10 '22 02:10

D'Arcy Rittich