简体   繁体   中英

MySQL recursive query for finding all parents of a many-to-many relationship

Edit Please consider this before marking as duplicate

The common CTE or repeated joins or similar solutions does not fully satisfy the premise of the problem. Those solutions work for one and only one root (product) while the question at hand, asks for all roots within a table or query to be traversed and flattened, without a loop.

Problem Definition

I have three tables that define the characteristics of some products:

  • specifications
+----|------------------|----------|--------+
| id |             name |     type | status |
|----|------------------|----------|--------|
|  1 |           height |    float |      0 |
|  2 |            width |    float |      0 |
|  3 |           length |    float |      0 |
|  4 |           weight |    float |      0 |
|  5 |           colour |      int |      0 |
|  6 |         material |      int |      0 |
|  7 |     manufacturer |      int |      0 |
|  8 |       durability |    float |      0 |
|  9 |     battery_type |      int |      0 |
| 10 | battery_capacity |    float |      0 |
| 11 |     connectivity | set<int> |      0 |
| 12 |             page |      int |      0 |
| 13 |             name |   string |      0 |
| 14 |      description |   string |      0 |
+----|------------------|----------|--------+
  • groups
+----|-------------------|--------+
| id |              name | status |
|----|-------------------|--------|
|  1 |         cellphone |      0 |
|  2 |          notebook |      0 |
|  3 |          portable |      0 |
|  4 |       workstation |      0 |
|  5 |                pc |      0 |
|  6 |          computer |      0 |
|  7 | electronic_device |      0 |
|  8 |              book |      0 |
|  9 |          sizeable |      0 |
| 10 |         volumable |      0 |
| 11 |           general |      0 |
+----|-------------------|--------+
  • specification_groups
+----|----------|------------------|--------+
| id | group_id | specification_id | status |
|----|----------|------------------|--------|
|  1 |       11 |               13 |      0 |
|  2 |       11 |               14 |      0 |
|  3 |       11 |                5 |      0 |
|  4 |       10 |                1 |      0 |
|  5 |        9 |                2 |      0 |
|  6 |        9 |                3 |      0 |
|  7 |        8 |               12 |      0 |
|  8 |        3 |                6 |      0 |
|  9 |        3 |                9 |      0 |
| 10 |        3 |               10 |      0 |
| 11 |        7 |                7 |      0 |
| 12 |        7 |               11 |      0 |
+----|----------|------------------|--------+
  • group_groups
+----|----------|--------------------|--------+
| id | group_id | group_reference_id | status |
|----|----------|--------------------|--------|
|  1 |        3 |                  1 |      0 |
|  2 |        3 |                  2 |      0 |
|  3 |        3 |                  8 |      0 |
|  4 |        6 |                  4 |      0 |
|  5 |        6 |                  5 |      0 |
|  6 |        7 |                  1 |      0 |
|  7 |        7 |                  2 |      0 |
|  8 |        7 |                  4 |      0 |
|  9 |        7 |                  5 |      0 |
| 10 |        9 |                  7 |      0 |
| 11 |        9 |                  8 |      0 |
| 12 |       10 |                  7 |      0 |
| 12 |       11 |                  7 |      0 |
| 12 |       11 |                  8 |      0 |
+----|----------|--------------------|--------+
  • product_groups
+----|--------|-------|--------+
| id |   name | group | status |
|----|--------|-------|--------|
|  1 | phone1 |     1 |      0 |
|  2 |  book1 |     8 |      0 |
+----|--------|-------|--------+

Ideally, I want to get all specification attributes for a product which the status along all tree-lines would be 0, but just to know what groups does a product is in is acceptable.

A result may look like this:

  • result
+---------|-------------|--------------|-------------------|----------|--------+
| row_num |  product_id | product_name |     product_group | group_id | status |
|---------|-------------|--------------|-------------------|----------|--------|
|       1 |           1 |       phone1 |         cellphone |        1 |      0 |
|       2 |           1 |       phone1 |          portable |        3 |      0 |
|       3 |           1 |       phone1 | electronic_device |        7 |      0 |
|       4 |           1 |       phone1 |           sizable |        9 |      0 |
|       5 |           1 |       phone1 |         volumable |       10 |      0 |
|       6 |           1 |       phone1 |           general |       11 |      0 |
|       7 |           2 |        book1 |              book |        8 |      0 |
|       8 |           2 |        book1 |          portable |        3 |      0 |
|       9 |           2 |        book1 |           sizable |        9 |      0 |
|      10 |           2 |        book1 |           general |       11 |      0 |
+---------|-------------|--------------|-------------------|----------|--------+

Here's the answer if anyone else wants to do the same. The trick was to use the non-recursive initial selection parameters as constants in the recursive one:

    with recursive 
        q as (
            select * from product_groups as pg where ur.`status` = 0 
        )
        , ancestors as (
            (
                select q.id as `product_id`, q.product_name as `product_name`, gg.group_id as `group_id`, gg.group_reference_id as `group_reference_id`
                from group_groups as gg
                    join q on gg.group_id = q.group_id
                where gg.`status` = 0
            )
            union
            (
                select q.id as `product_id`, q.product_name as `product_name`, null as `group_id`, q.group_id as `group_reference_id`
                from q
            )
            union
            (
                select a.id as `product_id`, a.product_name as `product_name`, gg.group_id as `group_id`, gg.group_reference_id as `group_reference_id`
                from group_groups as gg, ancestors as a
                where gg.group_id = a.group_reference_id and gg.`status` = 0
            )
        )
    select distinct
        t.id as `product_id`, t.`group_id` as `group_id`, t.specification_id as `specification_id`, t.product_name as `product_name`, t.group_name as `group_name`, s.name as `specification_name`
    from 
    (
        select a.product_id, a.group_reference_id as `group_id`, sg.specification_id as `specification_id`, a.product_name, g.name as `group_name`
        from ancestors as a
            right join specification_groups as sg on a.group_reference_id = sg.group_id
        where a.product_id is not null and sg.`status` = 0
    ) as t
        join specifications as s on t.specification_id = s.id
    order by product_id asc, group_id, asc, specification_id asc
    ;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM