我正在使用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又是类型号的对象,与数字表完全相同。
我已尝试过各种解决问题的尝试,但我似乎无法完全理解。任何帮助将不胜感激。
此外,欢迎提出任何改进的一般性意见。
答案 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
。