Oracle PL-SQL-消除对象表中的间隙
将Oracle PL-SQL-消除对象表中的间隙,oracle,plsql,Oracle,Plsql,将T_ATTRIBUTEPAGE_ATTRIBUTELIST作为如下对象表: CREATE OR REPLACE TYPE "T_ATTRIBUTEPAGE_ATTRIBUTELIST" IS TABLE OF o_ATTRIBUTEPAGE_ATTRIBUTELIST; CREATE OR REPLACE TYPE "O_ATTRIBUTEPAGE_ATTRIBUTELIST" IS OBJECT ( WizAttrEditID NUMBER, Inte
T_ATTRIBUTEPAGE_ATTRIBUTELIST
作为如下对象表:
CREATE OR REPLACE TYPE "T_ATTRIBUTEPAGE_ATTRIBUTELIST" IS TABLE OF o_ATTRIBUTEPAGE_ATTRIBUTELIST;
CREATE OR REPLACE TYPE "O_ATTRIBUTEPAGE_ATTRIBUTELIST" IS OBJECT (
WizAttrEditID NUMBER,
InternalIndex NUMBER,
DimensionObjectID NUMBER,
AttributeName VARCHAR2(50),
AttributeLabel VARCHAR2(50),
AttributeType NUMBER,
AttributeLength VARCHAR2(50),
MandatoryAttribute NUMBER,
ReadOnly NUMBER,
Name VARCHAR2(2000),
Num NUMBER,
IsModified NUMBER,
Colour NUMBER);
attributelist t_attributepage_attributelist;
FOR i in 1..attributelist.COUNT+1 LOOP
IF attributelist.EXISTS(i) THEN
j := j+1;
ls_attribute_list.EXTEND;
ls_attribute_list(j) := attributelist(i);
END IF;
END LOOP;
attributelist := ls_attribute_list;
我正在从表中删除一项:
attributelist.DELETE(ln_attrib_pos_function);
然后,如果我尝试遍历我的列表
找不到数据
我也尝试了FIRST
和LAST
方法,但结果是一样的。
我想要的是以某种方式使表再次稠密,以便在删除后可以进行后续迭代,而不会出现任何错误。我尝试创建新表,并使用EXISTS
方法消除与初始表的差距,但我得到了
超额订阅
我认为这是因为循环跳过了删除的项,所以I索引会转向
从10到12,如果删除的项目位于索引11
FOR i in 1..attributelist.COUNT LOOP
IF attributelist.EXISTS(i) THEN
ls_attribute_list.EXTEND;
ls_attribute_list(i) := attributelist(i);
ELSE
CONTINUE;
END IF;
END LOOP;
attributelist := ls_attribute_list;
基本上,我希望在删除一个或多个项目后,初始的
属性列表
可以进行迭代。最好的解决方案是,如果要从集合中删除,那么它们可以是稀疏的,并使用第一个/最后一个/下一个方法进行迭代:
i := attributeList.FIRST;
LOOP
-- do stuff
EXIT WHEN i = attributeList.LAST;
i := attributeList.NEXT(i);
END LOOP;
是的,对于attributeList.FIRST中的i,键入比稍微多一些。。属性列表。最后一个循环。。。做些事情。。。端环代码>但不会太多,而且当您忘记处理几个过程之前调用的删除时,您的应用程序逻辑不会突然在随机位置中断
但是,如果要创建稀疏集合的非稀疏副本,可以执行以下操作:
CREATE FUNCTION compactCollection(
list IN T_ATTRIBUTEPAGE_ATTRIBUTELIST,
) RETURN T_ATTRIBUTEPAGE_ATTRIBUTELIST DETERMINISTIC
IS
BEGIN
IF list IS NULL THEN
RETURN NULL;
END IF;
RETURN T_ATTRIBUTEPAGE_ATTRIBUTELIST()
MULTISET UNION list;
END;
/
但对于稀疏集合,最好只使用适当的迭代方法
更新
运算符将连接两个返回新集合(与操作数类型相同)的集合。浓缩的一个副作用是产生的收集物很密集
因此,将空集合与稀疏集合连接将生成稀疏集合的压缩(密集)版本
attributeList := T_ATTRIBUTEPAGE_ATTRIBUTELIST() MULTISET UNION attributeList;
将给出相同的元素(顺序相同),但只压缩元素索引
(如果您想使用FIRST/LAST/NEXT
迭代获得更长版本的代码,请参见)这应该可以
FOR i in attributelist.first..attributelist.last LOOP
如果在中间删除代码>计数< /代码>减少。代码>第一个
和最后一个
和存在
是您需要的
编辑:但为了避免在已删除的条目上循环,您可以按照MT0的建议执行,并使用NEXT
i := attributelist.first;
LOOP
dbms_output.put_line(attributelist(i).WizAttrEditID);
exit when i = attributelist.last;
i := attributelist.next(i);
END LOOP;
使用以下例行程序对其进行测试:
set serveroutput on
declare
attributelist t_attributepage_attributelist;
ls_attribute_list t_attributepage_attributelist;
i number;
begin
attributelist := t_attributepage_attributelist();
ls_attribute_list := t_attributepage_attributelist();
attributelist.extend;
attributelist(1) := O_ATTRIBUTEPAGE_ATTRIBUTELIST(1,null,null,null,null,null,null,null,null,null,null,null,null);
attributelist.extend;
attributelist(2) := O_ATTRIBUTEPAGE_ATTRIBUTELIST(2,null,null,null,null,null,null,null,null,null,null,null,null);
attributelist.extend;
attributelist(3) := O_ATTRIBUTEPAGE_ATTRIBUTELIST(3,null,null,null,null,null,null,null,null,null,null,null,null);
attributelist.extend;
attributelist(4) := O_ATTRIBUTEPAGE_ATTRIBUTELIST(4,null,null,null,null,null,null,null,null,null,null,null,null);
attributelist.delete(1);
attributelist.delete(3);
dbms_output.put_line('count='||attributelist.count||' last='||attributelist.last);
i := attributelist.first;
LOOP
exit when i is null; -- in case everything is deleted
dbms_output.put_line(attributelist(i).WizAttrEditID);
exit when i = attributelist.last;
i := attributelist.next(i);
END LOOP;
end;
输出:
count=2 last=4
2
4
PL/SQL procedure successfully completed.
我所做的和似乎起作用的是使用一个辅助对象表,我用删除的项迭代原始数组,然后消除一些辅助列表中的间隙,然后将该列表分配回原始数组,类似这样:
CREATE OR REPLACE TYPE "T_ATTRIBUTEPAGE_ATTRIBUTELIST" IS TABLE OF o_ATTRIBUTEPAGE_ATTRIBUTELIST;
CREATE OR REPLACE TYPE "O_ATTRIBUTEPAGE_ATTRIBUTELIST" IS OBJECT (
WizAttrEditID NUMBER,
InternalIndex NUMBER,
DimensionObjectID NUMBER,
AttributeName VARCHAR2(50),
AttributeLabel VARCHAR2(50),
AttributeType NUMBER,
AttributeLength VARCHAR2(50),
MandatoryAttribute NUMBER,
ReadOnly NUMBER,
Name VARCHAR2(2000),
Num NUMBER,
IsModified NUMBER,
Colour NUMBER);
attributelist t_attributepage_attributelist;
FOR i in 1..attributelist.COUNT+1 LOOP
IF attributelist.EXISTS(i) THEN
j := j+1;
ls_attribute_list.EXTEND;
ls_attribute_list(j) := attributelist(i);
END IF;
END LOOP;
attributelist := ls_attribute_list;
如果您创建了一个包含n
项的列表,并删除了中间的n-2
项,则使用此方法,即使只有2项(FIRST
和LAST
),您仍将迭代n
次在列表中。如果n
很小或不是很稀疏,则这可能无关紧要,但如果n
很大且非常稀疏,则您要求它重复循环并执行许多不必要的检查。仅一次迭代的延迟可能不会有太大问题,但如果频繁调用,则可以使用FIRST/NEXT/LAST
轻松重构代码以消除不必要的检查。我同意。你的解决方案很好。我的意图是指出错误的原因,而不涉及其他实际考虑。我记住,在相同的情况下,其他人会带着相同的错误信息来到这个页面,并且很容易理解问题所在;然而,这完全是关于为什么会发生这种情况,所以我把那个细节放在那个里了。我不知道有一个更早的问题。在这种情况下,这完全有道理。你的解决方案肯定更好。虽然我想知道集合中有多少个条目会比使用NEXT
在所有条目上循环慢。但这也是风格和干净编码的问题。如果列表中有4项并删除了第一项,则COUNT
将从4
减少到3
,但LAST
的值保持为4
,因此如果从1循环。。list.COUNT
并测试该项是否存在您将错过列表末尾索引大于COUNT
的项。我同意正确迭代的方法,但我正在将此代码添加到现有过程中,如果将来有人不习惯这种迭代并尝试在列表上迭代,则会删除它将中断,我是否可以创建一个新数组而不与原始数组有间隙,然后将此辅助数组分配回原始数组?这仅在删除单个项目(因此COUNT+1=LAST
)时有效。您最好先在attributelist.FIRST中为i使用。。attributelist.LAST循环
。另外,ELSE CONTINUE
是多余的,因为在循环之后你没有做任何事情。你也可以用attributelist:=T\u ATTRIBUTEPAGE\u attributelist()MULTISET UNION attributelist这将压缩数组。因此基本上,MULTISET UNION
将使列表变得密集?将连接两个集合(嵌套表)-连接的一个副作用是生成的集合是密集的。这在我的回答中是压缩集合函数的一部分,但我会尽量让它更清晰。