Finding parent by set of children
-
29-09-2020 - |
Вопрос
Given a many-to-many relationship (product
/component
), I need to find a product (or all products) that are composed from the given set of components. I can't wrap my head around the SQL query for this. A general solution, but if there is anything particular to SQLServer, that will work too.
Update: assume the standard many-to-many ternary mapping.
Решение
This is a case of "relational division". I tagged the question accordingly, you can find basic information in the tag info.
You did not provide details. Assuming a standard many-to-many (n:m) relationship between products and components which is implemented with a product_component
table, a basic solution could look like this:
SELECT c1.product_id
FROM product_component c1
JOIN product_component c2 ON c2.product_id = c1.product_id
JOIN product_component c3 ON c3.product_id = c1.product_id
WHERE c1.component_id = 1
AND c2.component_id = 2
AND c3.component_id = 3;
This needs at least an index on product_id
and on component_id
to be fast, ideally on (component_id, product_id)
.
There are many other ways. The best query depends on your exact table definition and data distribution. We assembled an arsenal of queries under this related question on SO, for Postgres / MySQL, but the SQL is (mostly) the same:
The above query finds products that contain at least the components with component_id
1
, 2
and 3
. To find products that consist of those components exactly (no others components):
SELECT c1.product_id
FROM product_component c1
JOIN product_component c2 ON c2.product_id = c1.product_id
JOIN product_component c3 ON c3.product_id = c1.product_id
LEFT JOIN product_component c0
ON c0.product_id = c1.product_id
AND c0.component_id NOT IN (c1.component_id
, c2.component_id
, c3.component_id)
WHERE c1.component_id = 1
AND c2.component_id = 2
AND c3.component_id = 3
AND c0.component_id IS NULL -- no other component
Again, there are various ways exclude products with additional components: