Reputation: 91
I'm quite a begginer and I have two tables: "product" and "product attributes".
Here's some imaginary data (the actual stuff involves more tables )
Products Table:
product_id | product_name
10 | aaa
11 | bbb
12 | ccc
Product Attributes Table:
attribute_id | product_id
21 | 10
23 | 10
24 | 10
21 | 11
24 | 11
21 | 12
25 | 12
Where each product has more than one possible attribute. I have a list of attribute ids like (21,10,25)
and I need to select all products whose attributes are a subset of that list.
Is it possible to do this in one query?
When I filter for (21,24) desired output is to return only product 11 (bbb)
When I filter for (21,23,24) desired output is to return products 10 and 11.
When I filter for (21) desired output is to return none (because all products have at least one other attribute).
Upvotes: 9
Views: 4493
Reputation: 91
let me post simple imaginary data ( the actual stuff involves more tables )
table products
product_id | product_name
10 | aaa
11 | bbb
table product_attribute
attribute_id | product_id <br>
21 | 10
23 | 10
24 | 10
21 | 11
24 | 11
i want that:
(21,24)
to be returned only product 11 (bbb)(21,23,24)
to be returned both products(21)
only to be returned none ( because no product has only that attribute )Upvotes: -1
Reputation: 77400
Until MySQL supports the EXCEPT
query combination,
SELECT product_id
FROM attributes
WHERE product_id NOT IN (
SELECT product_id
FROM attributes
WHERE attribute_id NOT IN (21, 23, 24)
)
GROUP BY product_id
UNION
SELECT id
FROM products AS p
LEFT JOIN attributes AS a
ON p.id = a.product_id
WHERE a.product_id IS NULL
If you wish to have only the products with all the given attributes, add a HAVING COUNT(*)=n
clause to the first outer query, where 'n' is the length of the attribute list.
Upvotes: 1
Reputation: 91
Based on your insight guys, I optimized it even further and used only 1 COUNT statement like this:
SELECT * ,COUNT(p.product_id) AS c FROM product_attribute pa
LEFT JOIN products p ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter_list)
GROUP BY pa.product_id
HAVING c=0
Does it work ? :)
Edit:
That code doesn't return the product's name or other fields it might have. This is the correct one:
SELECT * ,COUNT(pa.product_id ) AS c FROM products p
LEFT JOIN product_attribute pa ON pa.product_id = p.product_id AND pa.attribute_id NOT IN ($filter)
GROUP BY p.product_id
HAVING c=0
Upvotes: 0
Reputation: 1742
select
P.id,
P.name,
count(P.id) as matched_attr_count,
count(PA.a_id) as total_attr_count
from
product_attributes PA
left join product P on P.id = PA.p_id and PA.a_id in (21,23,24)
group by
PA.p_id
having
matched_attr_count = total_attr_count;
Upvotes: 1
Reputation: 11
This should return only those id's where all attributes for each id are completely contained within the list:
select attribute_match.id_product from
(select id_product, count(*) c from attributes
where id_attribute in (21, 10, 25)
group by id_product) attribute_match,
(select id_product, count(*) c_count from attributes
group by id_product) attribute_total
where attribute_match.id_product = attribute_total.id_product
and attribute_match.c = attribute_total.c
Upvotes: 1
Reputation: 3318
If you pretend that your filter is in a table:
select *
from product p
where not exists (
select 1
from attributes a
where a.product_id = p.product_id
and not exists(
select 1
from filter f
where f.id_attribute = a.id_attribute))
If it was in a constructed query:
select *
from product p
where not exists (
select 1
from attributes a
where a.product_id = p.product_id
and attribute_id not in (<list>))
This is off the top of my head, so may have typos.
Upvotes: 5
Reputation: 150108
Assuming your product table is called Product and the ID column in that table is just called Id:
SELECT * from Product p where p.Id IN
(Select id_product from ProductAttributes where id_attribute in (21, 23, 24))
Upvotes: 1