PL / SQL Join Collection对象问题

时间:2016-03-24 13:51:11

标签: object collections plsql oracle11g

我正在使用Oracle 11g数据库,版本11.2.0.3.0 - 64位生产

我编写了以下过程,该过程使用游标从名为benefit_info的表中收集批量的benefits_id(简称为NUMBER类型)。对于每批中的每个benefit_id,我需要获取相关客户,然后执行各种计算等。到目前为止,我有以下内容:

library(data.table)
dt=data.table(items=permn(1:n))
dt[,scores:=lapply(items,function(x) m[E[,x]])]
dt[,totalScore:=lapply(scores,sum)]
dt
#       items            scores totalScore
#1: 1,2,3 0.472,0.239,0.517      1.228
#2: 1,3,2 0.472,0.039,0.223      0.734
#3: 3,1,2 0.658,0.064,0.223      0.945
#4: 3,2,1 0.658,0.239,0.994      1.891
#5: 2,3,1 0.326,0.039,0.994      1.359
#6: 2,1,3 0.326,0.064,0.517      0.907

如上面的代码所示,FOR indx IN 1 .. LOOP非常慢,特别是因为有数百万的benefit_ids。但是,我知道我可以用以下内容替换整个FOR LOOP:

CREATE OR REPLACE PROCEDURE ben_correct(in_bulk_collect_limit IN PLS_INTEGER DEFAULT 1000)
IS

   TYPE ben_identity_rec IS RECORD
      (
          life_scd_id   NUMBER,
          benefit_id    NUMBER
      );

   TYPE ben_identity_col IS TABLE OF ben_identity_rec INDEX BY PLS_INTEGER;

   life_col ben_identity_col;

   ben_id NUMBER;

   CURSOR benefit_cur
   IS
     SELECT benefit_id FROM benefit_info;

    TYPE benefit_ids_t IS TABLE OF NUMBER INDEX BY PLS_INTEGER;

    benefit_ids benefit_ids_t;

    PROCEDURE get_next_set_of_incoming(out_benefit_ids OUT NOCOPY benefit_ids_t)
    IS
    BEGIN
       FETCH benefit_cur
           BULK COLLECT INTO out_benefit_ids
           LIMIT in_bulk_collect_limit;
    END;

BEGIN
   OPEN benefit_cur;

   LOOP
      get_next_set_of_incoming(benefit_ids);

      /*
       The code below is too slow as each benefit_id is considered
       individually. Want to change FOR LOOP into LEFT JOIN of benefit_ids
       */
      FOR indx IN 1 .. benefit_ids.count LOOP

          ben_id := benefit_ids(indx);

          SELECT c.life_scd_id, c.benefit_id
          BULK COLLECT INTO life_col
          FROM customer c
          WHERE c.benefit_id = ben_id;

          -- Now do further processing with life_col
      END LOOP;

      EXIT WHEN benefit_ids.count = 0;

   END LOOP;

   CLOSE benefit_cur;

END;
/

但是,为了实现这一点,我认为我需要在模式级别声明一个Object类型,因为我认为在SELECT查询中你可以加入纯表或对象集合。因此,从我删除的程序

SELECT c.life_scd_id, c.benefit_id
BULK COLLECT INTO life_col
FROM customer c
LEFT JOIN table(benefit_ids) b
WHERE b.benefit_id IS NOT NULL;

而是在模式级别我定义了

TYPE benefit_ids_t IS TABLE OF NUMBER INDEX BY PLS_INTEGER;

我修改后的代码基本上变为:

CREATE OR REPLACE TYPE ben_id FORCE AS object
    (
      benefit_id number
    );

CREATE OR REPLACE TYPE benefit_ids_t FORCE AS TABLE OF ben_id;

然而,这会产生ORA-24344和PLS-00386错误,即在FETCH游标和INTO变量之间的'OUT_BENEFIT_IDS'处发现类型不匹配。

我有点明白,抱怨现在good_ids_t是一个ben_ids表,而ben_ids又是类型号的对象,与数字表完全相同。

我已尝试过各种解决问题的尝试,但我似乎无法完全理解。任何帮助将不胜感激。

此外,欢迎提出任何改进的一般性意见。

1 个答案:

答案 0 :(得分:1)

您不需要您的表类型是包含数字字段的对象,它可以只是一个数字表:

CREATE OR REPLACE TYPE benefit_ids_t FORCE AS TABLE OF number;

或者你可以使用像sys.odcinumberlist这样的内置类型,但是你自己控制的类型也不是坏事。

您不想使用动态SQL;这样:

  sql_str := 'SELECT c.life_scd_id, c.benefit_id
      FROM customer c
      LEFT JOIN table(benefit_ids) b
      WHERE b.benefit_id IS NOT NULL';

  EXECUTE IMMEDIATE sql_str BULK COLLECT INTO life_col;

将无效,因为benefit_ids在执行该动态语句时不在作用域中。你可以静态地做到这一点:

SELECT c.life_scd_id, c.benefit_id
BULK COLLECT INTO life_col
FROM table(benefit_ids) b
JOIN customer c
ON c.benefit_id = b.column_value;

更接近原始代码中的内容。

您的EXIT也位于错误的位置 - 当它找不到任何内容时,它会尝试处理循环中的行。我根本不打扰单独的提取过程,直接在循环中使用fetch更容易:

BEGIN
   OPEN benefit_cur;

   LOOP
      FETCH benefit_cur
         BULK COLLECT INTO benefit_ids
         LIMIT in_bulk_collect_limit;

      EXIT WHEN benefit_ids.count = 0;

      SELECT c.life_scd_id, c.benefit_id
      BULK COLLECT INTO life_col
      FROM table(benefit_ids) b
      JOIN customer c
      ON c.benefit_id = b.column_value;

      -- Now do further processing with life_col

   END LOOP;

   CLOSE benefit_cur;
END;

如果你真的想要你的对象类型,你可以保留它,但是你需要通过它的默认构造函数让你的游标返回该对象的实例:

CURSOR benefit_cur
IS
  SELECT ben_id(benefit_id) FROM benefit_info;

客户查询联接将是:

SELECT c.life_scd_id, c.benefit_id
BULK COLLECT INTO life_col
FROM table(benefit_ids) b
JOIN customer c
ON c.benefit_id = b.benefit_id;

由于它是对象类型,您可以引用它的字段名称benefit_id,而不是标量类型表中的通用column_value