Вопрос

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:

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top