如何在批量收集循环中进行条件处理?
Posted
技术标签:
【中文标题】如何在批量收集循环中进行条件处理?【英文标题】:How to do conditional processing in a bulk collect loop? 【发布时间】:2019-05-27 22:13:44 【问题描述】:我们有Oracle 11G
,我正在尝试使用bulk collect
将数据从一个表移动到另一个表。问题是当我试图评估来自原产地的一个字段是否为空时,我的包裹无效。我有什么:
声明:
CREATE OR REPLACE PACKAGE MYSCHEMA.MYPKG AS
CURSOR CUR_MYDATA IS
SELECT
o.name,
o.last_name,
o.id,
o.socnum
FROM
origin o
WHERE
1=1
AND o.name like upper ('a%');
TYPE t_name IS TABLE OF origin.name%TYPE;
TYPE t_lastname IS TABLE OF origin.last_name%TYPE;
TYPE t_id IS TABLE OF origin.id%TYPE;
TYPE t_socnum IS TABLE OF origin.socnum%TYPE;
l_name t_name;
l_lastname t_lastname;
l_id t_id;
l_socnum t_socnum;
PROCEDURE MYPROCEDURE;
END MYPKG;
主体:
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.MYPKG AS
PROCEDURE MYPROCEDURE IS
BEGIN
OPEN CUR_MYDATA;
LOOP
FETCH CUR_MYDATA BULK COLLECT INTO l_name,l_lastname,l_id,l_socnum;
forall i IN 1 .. l_name.COUNT
IF ( l_socnum(i) IS NULL)
THEN (select oo.socnum from other_origin where oo.id=l_id(i))
END IF;
INSERT INTO destiny (
d_name,
d_lastname,
d_id,
d_socnum)
VALUES (
l_name(i),
l_lastname(i),
l_id(i),
l_socnum(i),
EXIT WHEN l_name.count = 0;
END LOOP;
END MYPROCEDURE;
END MYPKG;
但是当我检查身体状态时,它是INVALID
有什么想法吗?
【问题讨论】:
查看user_errors
视图,看看有什么问题。乍一看,你有一个select oo.socnum
,但没有into
;但是您不能(AFAIK)将逻辑作为forall
的一部分-我认为您的插入超出了该范围,这将导致几个错误。您的批量收集中也没有limit
,因此您只需循环一次。那么......你为什么不做一个简单的insert ... select
声明?
一个明显的错误与INSERT
语句末尾的l_socnum(i),
有关,应将其替换为l_socnum(i));
@AlexPoole 感谢您的提示,我正在做bulk collect
只是因为有人建议我这样做,没有特别的原因
做某事有理由总是好的,尤其是当某事可能导致次优性能时。这尤其适用于编写批量操作时,即使是很小的低效率的乘法效应也会对我们程序的效率产生很大影响。过早优化和不做我们知道会损害性能的事情是有区别的。
@APC 我肯定会的,只要我完成早会
【参考方案1】:
FORALL 不是一个循环结构:它不能从它的 DML 语句中分离出来。
当我试图评估来自 origin 的一个字段是否为空时
您需要循环填充的集合并在执行 FORALL ... INSERT 之前修复它。
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.MYPKG AS
PROCEDURE MYPROCEDURE IS
BEGIN
OPEN CUR_MYDATA;
LOOP
FETCH CUR_MYDATA BULK COLLECT INTO l_name,l_lastname,l_id,l_socnum;
EXIT WHEN l_name.count = 0;
for idx in 1 .. l_socnum.count() loop
IF l_socnum(idx) IS NULL THEN
select oo.socnum
into l_socnum(idx)
from other_origin
where oo.id = l_id(idx);
END IF;
end loop;
forall i IN 1 .. l_name.COUNT
INSERT INTO destiny (
d_name,
d_lastname,
d_id,
d_socnum)
VALUES (
l_name(i),
l_lastname(i),
l_id(i),
l_socnum(i));
END LOOP;
END MYPROCEDURE;
END MYPKG;
其他说明。
-
在执行提取后检查提取是否立即返回任何记录。否则,您的代码将尝试在空集合上执行代码,这将失败。
您应该基于目标表
%rowtype
定义一个集合:这比基于列定义和处理多个集合更简单。
此外,您的实际代码可能比您在此处发布的要复杂得多,但如果您有大量数据要转移,那么使用纯 SQL 而不是过程可以获得很多性能提升:
INSERT INTO DESTINY (
D_NAME,
D_LASTNAME,
D_ID,
D_SOCNUM
)
SELECT
o.name,
o.last_name,
o.id,
coalesce(o.socnum, oo.socnum)
FROM
origin o
left outer join other_origin oo
on oo.id = o.id
WHERE
1=1
AND o.name like upper ('a%');
【讨论】:
你的例子对我有用,谢谢,但是for idx in 1 .. l_socnum loop
我不得不for idx in 1 .. l_socnum.count loop
是的,很抱歉。当我们没有要编译的架构时,总是很难发现程序中的拼写错误。【参考方案2】:
FOR ALL 中不允许使用 IF 条件。
FOR ALL 可以执行单个 DML:INSERT、UPDATE 或 DELETE 语句,这些语句写在它之后。 for循环不正常。
你可以试试下面的代码:
包装:
CREATE OR REPLACE PACKAGE MYSCHEMA.MYPKG AS
CURSOR CUR_MYDATA IS
SELECT
O.NAME,
O.LAST_NAME,
O.ID,
-- ADDED THIS CASE STATEMENT
CASE
WHEN O.SOCNUM IS NOT NULL THEN O.SOCNUM
ELSE OO.SOCNUM
END AS SOCNUM
FROM
-- ADDED THIS LEF JOIN
ORIGIN O
LEFT JOIN OTHER_ORIGIN OO ON ( OO.ID = O.ID )
WHERE
1 = 1
AND O.NAME LIKE UPPER('a%');
TYPE T_NAME IS
TABLE OF ORIGIN.NAME%TYPE;
TYPE T_LASTNAME IS
TABLE OF ORIGIN.LAST_NAME%TYPE;
TYPE T_ID IS
TABLE OF ORIGIN.ID%TYPE;
TYPE T_SOCNUM IS
TABLE OF ORIGIN.SOCNUM%TYPE;
L_NAME T_NAME;
L_LASTNAME T_LASTNAME;
L_ID T_ID;
L_SOCNUM T_SOCNUM;
PROCEDURE MYPROCEDURE;
END MYPKG;
包体
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.MYPKG AS
PROCEDURE MYPROCEDURE IS
BEGIN
OPEN CUR_MYDATA;
FETCH CUR_MYDATA BULK COLLECT INTO
L_NAME,
L_LASTNAME,
L_ID,
L_SOCNUM
LIMIT 1000;
FORALL I IN 1..L_NAME.COUNT
--
-- REMOVED THIS CONDITION
--
-- IF ( l_socnum(i) IS NULL)
-- THEN (select oo.socnum from other_origin where oo.id=l_id(i))
-- END IF;
INSERT INTO DESTINY (
D_NAME,
D_LASTNAME,
D_ID,
D_SOCNUM
) VALUES (
L_NAME(I),
L_LASTNAME(I),
L_ID(I),
L_SOCNUM(I)
);
CLOSE CUR_MYDATA;
END MYPROCEDURE;
END MYPKG;
【讨论】:
感谢您的回答,我还没有测试过,但对我来说似乎是一个不错的选择以上是关于如何在批量收集循环中进行条件处理?的主要内容,如果未能解决你的问题,请参考以下文章