Oracle 中的递归 SQL 每行只分配一次数据

Recursive SQL in Oracle to allocate data only once per row

我在 Oracle 中有以下数据

    ITEM_CNT ID
    0        1
    1        1
    2        1
    3        1
    0        2
    2        2
    3        2
    0        3
    1        3
    2        3
    3        3
    4        3

我想要以下格式的数据。请注意,由于 ITEM_CNT 0 被 ID 1 占用,因此 ID 2 应占用下一个可用号码,即 2.Similarly ITEM_CNT 1 被 ID 3 占用,依此类推。

请注意,如果 item_cnt 已被一个 ID 使用,则不能被另一个 ID 使用。 也总是选择最少的 ITEM_CNT 可用。

    ITEM_CNT ID
    0        1
    2        2
    1        3

此外,请注意,我尝试了下面的方法,但是对于每增加一行,我都必须再编写一个递归代码,如果有人可以使下面的代码递归,那就太好了

        SELECT Min(m3.item_cnt) item_cnt,
           m3.id            id,
           m3.item          item
    FROM   my_fil_data m3
    WHERE  m3.item_cnt NOT IN (SELECT Min(m4.item_cnt)
                               FROM   my_fil_data m4
                               WHERE  m3.id > m4.id
                                      AND m4.item_cnt NOT IN (SELECT
                                          Min(m5.item_cnt)
                                                              FROM   my_fil_data m5
                                                              WHERE  m4.id > m5.id
                                                                     AND m5.item_cnt
                                                                         NOT IN
                                                             (SELECT
                                          Min(m6.item_cnt)
                                                              FROM   my_fil_data m6
                                                              WHERE  m5.id > m6.id
                                                              GROUP  BY m6.id)
                                                              GROUP  BY m5.id)
                               GROUP  BY m4.id)
    GROUP  BY id,
              item 

我能够使用一个函数和一个集合来获得答案。

首先,在架构级别定义一个集合。

create type list1 is table of number;

然后创建以下函数

    CREATE OR replace FUNCTION Fn_get_recursive_xyz2(p_id NUMBER)
    RETURN NUMBER
    AS
      l_result NUMBER;
      TYPE list2
        IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
      listrec  LIST2;
      listrec1 LIST1 := List1();
      l_num    NUMBER;
      l_mm     NUMBER;
      l_mn     NUMBER;
      l_cnt    NUMBER;
    BEGIN
        l_num := p_id - 1;

        IF p_id > 2 THEN
          FOR rec IN 1..l_num LOOP
              SELECT Count(1)
              INTO   l_cnt
              FROM   my_fil_data
              WHERE  id = rec;

              IF l_cnt = 0 THEN
                CONTINUE;
              END IF;

              SELECT Min(m3.item_cnt) item_cnt
              INTO   l_mm
              FROM   my_fil_data m3
              WHERE  m3.id = rec
                     AND m3.item_cnt NOT IN (SELECT *
                                             FROM   TABLE(listrec1));

              listrec1.extend;

              Listrec1(listrec1.last) := l_mm;

              SELECT Min(m3.item_cnt) item_cnt
              INTO   l_mn
              FROM   my_fil_data m3
              WHERE  m3.id = rec
                     AND m3.item_cnt NOT IN (SELECT Min(m4.item_cnt) item_cnt
                                             FROM   my_fil_data m4
                                             WHERE  m3.id > m4.id
                                             --and m4.item_cnt 
                                             -- not in (select * from table(listrec)
                                             --)
                                             GROUP  BY m4.id)
              GROUP  BY m3.id;

              listrec1.extend;

              Listrec1(listrec1.last) := l_mn;
          END LOOP;
        ELSIF ( p_id = 2 ) THEN
          SELECT Count(1)
          INTO   l_cnt
          FROM   my_fil_data
          WHERE  id = 1;

          IF l_cnt <> 0 THEN
            SELECT Min(m3.item_cnt) item_cnt
            INTO   l_mn
            FROM   my_fil_data m3
            WHERE  m3.id = 1
                   AND m3.item_cnt NOT IN (SELECT Min(m4.item_cnt) item_cnt
                                           FROM   my_fil_data m4
                                           WHERE  m3.id > m4.id
                                           --and m4.item_cnt 
                                           -- not in (select * from table(listrec)
                                           --)
                                           GROUP  BY m4.id)
            GROUP  BY m3.id;

            listrec1.extend;

            Listrec1(listrec1.last) := l_mn;
          END IF;
        END IF;

        SELECT Min(m3.item_cnt) item_cnt
        INTO   l_result
        FROM   my_fil_data m3
        WHERE  m3.id = p_id
               AND m3.item_cnt NOT IN (SELECT *
                                       FROM   TABLE(listrec1));

        listrec1.DELETE;

        RETURN l_result;
    END fn_get_recursive_xyz2; 

然后你可以像下面这样调用函数来得到想要的结果

   WITH fl
         AS (SELECT DISTINCT id AS id
             FROM   my_fil_data)
    SELECT Fn_get_recursive_xyz2(id) ITEM_CNT,
           id
    FROM   fl
    ORDER  BY id;