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 attributelistMULTISET UNION
将使列表变得密集?将连接两个集合(嵌套表)-连接的一个副作用是生成的集合是密集的。这在我的回答中是压缩集合函数的一部分,但我会尽量让它更清晰。