Reputation: 754
I'm currently working on objects that are linked through a many-to-any relationship (it means that many objects can be linked to... not many objects of the same type, but many objects which type can vary).
But, for this question, a many-to-many will be plenty enough.
So, let's say I have a ObjectA table, with the following :
CREATE TABLE (
id INT(11) NOT NULL,
label VARCHAR(30) NOT NULL,
-- primary keys and all the stuff
);
CREATE TABLE ObjectB (
id INT(11) NOT NULL,
label VARCHAR(30) NOT NULL,
-- primary keys and all the stuff
);
CREATE TABLE ObjectA_ObjectB (
objectA_id INT(11) NOT NULL,
objectA_type VARCHAR(250) NOT NULL, -- for the many to any
objectB_id INT(11) NOT NULL,
-- primary keys and all the stuff
);
Let's say I want to filter the ObjectA with 2 or three elements from ObjectB. You will think that I should use a IN
:
SELECT *
FROM ObjectA a
LEFT JOIN ObjectA_ObjectB relation ON a.id = relation.objectA_id AND "ObjectA" = relation.objectA_class
LEFT JOIN ObjectB b ON relation.objectB_id = b.id
WHERE b.id IN (1, 2, 3);
But, the thing is, with this request, it gets all the objectAs that are linked with at least ONE of the ObjectBs searched (here, 1, 2, and 3). But, what I want, are those that have all the ObjectB's 1, 2 and 3.
I already tried some things (like the ALL
possibility, or pondering to make a first filter outside of SQL), but it didn't give the expected results.
Any ideas ?
To sum up, I'd like to be able to do a bit like the issues from GitHub and how they are filtering their label. If you pick up a bunch of labels, only the issues that have all the selected labels are returned, rather than all the issues that have at least one label.
Thanks !
Upvotes: 1
Views: 1726
Reputation: 13110
From your comments it sounds like you don't want LEFT JOIN
which will return ALL ObjectA
s which match the WHERE
clause. As for the relation condition you have to count the matching rows:
SELECT *
FROM ObjectA a
JOIN ObjectA_ObjectB relation
ON a.id = relation.objectA_id
AND "ObjectA" = relation.objectA_class
JOIN ObjectB b
ON relation.objectB_id = b.id
GROUP BY a.id
HAVING COUNT(CASE WHEN b.id IN (1,2,3) THEN 1 ELSE NULL END)=3;
OR:
SELECT *
FROM ObjectA a
JOIN ObjectA_ObjectB relation
ON a.id = relation.objectA_id
AND "ObjectA" = relation.objectA_class
JOIN ObjectB b
ON relation.objectB_id = b.id
AND b.id IN (1,2,3)
GROUP BY a.id
HAVING COUNT(*)=3;
If you don't actually need the ObjectB data in this query you can simplify this even further:
SELECT a.*
FROM ObjectA a
JOIN ObjectA_ObjectB relation
ON a.id = relation.objectA_id
AND "ObjectA" = relation.objectA_class
AND relation.objectB_id IN (1,2,3)
GROUP BY a.id
HAVING COUNT(*)=3;
This assumes that you can only have one of each ObjectB.id
linked to an ObjectA
through the pivot table.
Upvotes: 1
Reputation: 1831
A solution is to count the matching conditions in an having clause.
SELECT *
FROM ObjectA a
JOIN ObjectA_ObjectB relation ON a.id = relation.objectA_id AND "ObjectA" = relation.objectA_class
JOIN ObjectB b ON relation.objectB_id = b.id
WHERE b.id IN (1, 2, 3) GROUP BY a.id HAVING COUNT(b.id)=3;
See sqlfidle example.
Upvotes: 1