【问题标题】:PL/SQL Join Collection Object problemsPL/SQL 连接集合对象问​​题
【发布时间】:2016-07-12 03:18:57
【问题描述】:

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

我已经编写了以下过程,它使用游标从名为benefit_info 的表中收集成批的benefit_ids(只是NUMBER 类型)。对于每批中的每个benefit_id,我需要获取关联的客户,然后进行各种计算等。到目前为止,我有以下内容:

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;
/

如上面的代码所示,FOR indx IN 1 .. LOOP 非常慢,尤其是在有数百万个 benefit_id 的情况下。但是,我知道我可以将整个 FOR LOOP 替换为:

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;

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

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;

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

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

   sql_str VARCHAR2(1000);

   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;

   CURSOR benefit_cur
   IS
     SELECT benefit_id FROM benefit_info;

    --- benefit_ids_t has now been declared at schema level
    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);

      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;

      -- Now do further processing with life_col

      EXIT WHEN benefit_ids.count = 0;

   END LOOP;

   CLOSE benefit_cur;

END;
/

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

我有点理解,它抱怨说 benefit_ids_t 现在是一张 ben_id 表,而 ben_id 又是 number 类型的对象,这与数字表不太一样。

我已经尝试了各种尝试来解决这些问题,但我似乎无法完全正确。任何帮助将不胜感激。

此外,欢迎任何需要改进的通用 cmets。

【问题讨论】:

    标签: object collections plsql oracle11g


    【解决方案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 过程而烦恼,直接在循环中进行 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

    【讨论】:

    • 可爱,我没有意识到表类型可以简单地声明为数字表,而不是恰好是数字的对象表。然后我就不会知道使用 b.column_value。最后,感谢您指出 EXIT 在错误的位置 - 我至少应该发现这一点
    • 只有一个问题。您完成 FROM tables(benefit_ids) b 然后执行 JOIN 是否重要?是否可以反过来,即 FROM customers with the JOIN on table(benefit_ids)?
    • @user2948208 - 不,对于内部连接实际上并不重要,优化器会选择最佳顺序(尽管它不知道表集合表达式的基数);但从逻辑上讲,您不只是在寻找具有集合中 ID 的客户吗?您在第二个区块中有一个左连接,但我不知道为什么 - 您真的想要没有在集合中具有好处 ID 的客户吗?这将使收集毫无意义......
    • 是的,同意。最后,FETCH 过程的唯一原因仅仅是因为代码最初是由 Steven Feuerstein 启发和/或提供的。像你一样,我不会把它分成一个单独的过程来处理一个 FETCH 例程,但我想现在,benefit_ids_t 已经被更一般地重新定义为一个数字表(而不是以前的名为 benefit_ids 的对象),我猜 FETCH 过程可以被多个游标调用。我想现在是可能的
    • 该过程指定提取来自benefit_cur 游标,所以不,它不能重复用于多个游标。如果您在多个地方使用相同的光标,那么像这样重构它可能会有意义,但不确定它是否真的增加了很多。但这取决于品味/标准/等,功能上并不重要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 2012-09-02
    相关资源
    最近更新 更多