【问题标题】:Recursive SQL in Oracle to allocate data only once per rowOracle中的递归SQL每行仅分配一次数据
【发布时间】:2020-04-08 09:52:45
【问题描述】:

我在 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。同样 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 

【问题讨论】:

  • 为什么ID 2 没有得到ITEM_CNT 1?为什么ID 3 会得到 2 个ITEM_CNT 值?
  • 因为 ID 2 没有 ITEM_CNT 1
  • 但你的问题是“注意,因为 ITEM_CNT 0 被 ID 1 占用,因此 ID 2 应该采用下一个可用数字,即 2”
  • 我已经编辑了问题

标签: sql oracle recursive-query


【解决方案1】:

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

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

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; 

【讨论】:

    猜你喜欢
    • 2017-12-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 2016-05-03
    • 2019-08-18
    • 2017-09-13
    • 2022-08-19
    • 2018-12-24
    相关资源
    最近更新 更多