根据匹配表对记录进行分类

时间:2019-02-25 10:07:29

标签: sql oracle

我有两个表:ITEMS和MATCHING_ITEMS,如下所示:

ITEMS:
|---------------------|------------------|
|          ID         |       Name       |
|---------------------|------------------|
|          1          |        A         |
|          2          |        B         |
|          3          |        C         |
|          4          |        D         |
|          5          |        E         |
|          6          |        F         |
|          7          |        G         |
|---------------------|------------------|

MATCHING_ITEMS:
|---------------------|------------------|
|        ID_1         |       ID_2       |
|---------------------|------------------|
|          1          |        2         |
|          1          |        3         |
|          2          |        3         |
|          4          |        5         |
|          4          |        6         |
|          5          |        6         |
|---------------------|------------------|

MATCHING_ITEMS表定义了彼此匹配的项目,因此属于同一组,即项目1,2和3彼此匹配并因此属于一个组,而对于项目4,5和6.项目7不具有属于任何组的匹配项。

我现在需要在ITEMS表上添加一个“组”列,其中每个组都包含一个唯一的整数,因此它将如下所示:

ITEMS:
|---------------------|------------------|------------------|
|          ID         |       Name       |       Group      |
|---------------------|------------------|------------------|
|          1          |        A         |        1         |
|          2          |        B         |        1         |
|          3          |        C         |        1         |
|          4          |        D         |        2         |
|          5          |        E         |        2         |
|          6          |        F         |        2         |
|          7          |        G         |       NULL       |
|---------------------|------------------|------------------|

到目前为止,我一直在使用存储过程来执行此操作,遍历MATCHING_ITEMS表中的每一行,并使用组值更新ITEMS表。问题是我最终需要对包含数百万条记录的表执行此操作,并且循环方法太慢了。

有没有一种方法可以在不使用循环的情况下实现这一目标?

3 个答案:

答案 0 :(得分:0)

您可以先使用minmax,然后使用dense_rank分配组号:

select id, name, dense_rank() over (order by mn, mx) grp
  from (
    select distinct id, name, 
           min(id_1) over (partition by name) mn, 
           max(id_2) over (partition by name) mx
      from items left join matching_items on id in (id_1, id_2))
  order by id

demo

答案 1 :(得分:0)

如果匹配表中有所有匹配项对,则可以使用最小ID来分配组。为此:

select i.*,
       (case when grp_id is not null
             then dense_rank() over (order by grp_id)
        end) as grouping
from items i left join
     (select mi.id_1, least(mi.id1, min(mi.id2)) as grp_id
      from matching_items mi
      group by mi.id_1
     ) mi
     on i.id = mi.id_1;

注意:仅当所有对在匹配项表中时,此方法才有效。否则,您将需要递归/分层查询以获取所有对。

答案 2 :(得分:0)

Matching_items表中的2,3和5,6对似乎是多余的,因为它们可以派生出来(如果我没看错你的问题的话)

这就是我的做法。我只是将您示例中的id_1作为组号重复使用:

    create table
    items (
     ID number,
    name varchar2 (2)
    );

    insert into items values (1, 'A');
    insert into items values (2, 'B');
    insert into items values (3, 'C');
    insert into items values (4, 'D');
    insert into items values (5, 'E');
    insert into items values (6, 'F');
    insert into items values (7, 'G');

    create table
    matching_items (
    ID number,
    ID_2 number
    );

    insert into matching_items values (1, 2);
    insert into matching_items values (1, 3);
    insert into matching_items values (2, 3);
    insert into matching_items values (4, 5);
    insert into matching_items values (4, 6);
    insert into matching_items values (5, 6);

    with new_grp as
    (
    select id, id_2, id as group_no
    from matching_items
    where id in (select id from items)
    and id not in (select id_2 from matching_items)),
    assign_grp as
    (
    select id, group_no
    from new_grp
    union
    select id_2, group_no
    from new_grp)
    select items.id, name, group_no
    from items left outer join assign_grp
    on items.id = assign_grp.id;