Reputation: 8078
I'm having some problems to get this query works as I expect.
I have three tables: products
, product_attributes
and attributes
.
The relation is obvious (A product can have multiple attributes)
products
---------
id
product_attributes
------------------
product_id
attribute_id
attributes
----------
id
name
What I want to achieve is to get those products that has a given list of attributes, BUT omit those products that only has a partial list of the desired attributes.
For example, having these products and attributes:
A query asking for those products with [blue,boy] would retrieve only Shoe 1
.
A query asking for those products with [blue] would not return anything.
Since now I was working with this query:
SELECT p.*, pa.attribute_id
FROM products AS p
LEFT JOIN product_attributes AS pa ON(pa.product_id=p.id)
WHERE
pa.attribute_id IN(' . implode(',', $attr_ids) . ')
GROUP BY p.id
HAVING count(pa.attribute_id)=' . count($attr_ids)
This fails when just an attribute is given because it will return any product having that attribute.
Upvotes: 1
Views: 1407
Reputation: 6513
-- PHP (or any other languaje) parts are hardcoded here!!!!
SELECT p.*, hma.howmuchattr
-- howmuchattr is needed by HAVING clause,
-- you can omit elsewhere (by surrounding SELECT or by programming languaje)
FROM products AS p
LEFT JOIN product_attributes AS pa ON pa.product_id = p.id
LEFT JOIN (
SELECT product_id, count(*) as howmuchattr
FROM product_attributes
GROUP BY product_id
) as hma on p.id = hma.product_id
WHERE
pa.attribute_id IN
(1,3) -- this cames from PHP (or any other languaje). Can be (1) for the other case
GROUP BY p.id
HAVING count(*) = howmuchattr;
see sqlfiddle here
see also this answer
Upvotes: 2
Reputation: 33945
Aside from any other issues, this query...
SELECT p.*
, pa.attribute_id
FROM products p
LEFT
-- OUTER (this keyword is optional in MySQL)
JOIN product_attributes pa
ON pa.product_id = p.id
WHERE pa.attribute_id IN('$attr_ids')
GROUP
BY p.id
HAVING COUNT(*) = $cnt;
... is logically identical to...
SELECT p.*
, pa.attribute_id
FROM products p
-- INNER (this keyword is also optional in MySQL)
JOIN product_attributes pa
ON pa.product_id = p.id
WHERE pa.attribute_id IN('$attr_ids')
GROUP
BY p.id
HAVING COUNT(pa.attribute_id) = $cnt;
In order to maintain the utility of the OUTER JOIN consider rewriting as follows...
SELECT p.*
, pa.attribute_id
FROM products p
LEFT
JOIN product_attributes pa
ON pa.product_id = p.id
AND pa.attribute_id IN('$attr_ids')
GROUP
BY p.id
HAVING COUNT(pa.attribute_id) = $cnt;
Upvotes: 0