PL-SQL 从对象表中删除项目

Posted

技术标签:

【中文标题】PL-SQL 从对象表中删除项目【英文标题】:PL-SQL Delete item from a table of Objects 【发布时间】:2016-06-09 11:28:08 【问题描述】:

我正在尝试从对象表类型的变量中删除一个项目:

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

我正在遍历对象列表,我检查了ls_attr_list.COUNT 是 16,我尝试在满足条件时删除一个项目,但我收到此错误:

ORA-01403:未找到数据

在这一行提出:ls_attr_list.Delete(i);

FOR i IN 1..ls_attr_list.COUNT LOOP
           BEGIN
               IF ls_attr_list(i).attributename = 'PROTECTION_ROLE' THEN
                   ls_attr_list.Delete(i);
                END IF;
               EXCEPTION WHEN OTHERS THEN
                 writelog(SQLERRM,'ERROR');
                 END;
            END LOOP;

ls_attr_list 使用输入参数初始化:

PROCEDURE AttribInit(geninfo        IN OUT    o_geninfo,
                     pageinfo       IN OUT    o_attributepage_pageinfo,
                     attributelist  IN OUT    t_attributepage_attributelist,
                     enumlist       IN OUT    t_attributepage_enumlist)

AS
ls_attr_list := attributelist;

删除一项后迭代列表时出现问题

i := attributelist.FIRST; 
LOOP 
IF attributelist(i).attributename = 'PROTECTION_ROLE' THEN attributelist.DELETE(i); 
END IF; 
EXIT WHEN i = attributelist.LAST; 
i := attributelist.NEXT(i);
 END LOOP; 
--second FOR
FOR i in 1..attributelist.COUNT LOOP 

--到达上一个删除项的索引时没有找到数据

writelog(attributelist(i).attributename,'attributename'); 
END LOOP;

我做错了什么,有什么想法吗??

【问题讨论】:

当您从集合中间删除值时,您可能需要使用FIRST/LAST/NEXT 而不是使用FOR 循环,因为COUNT < LAST 不知道为什么,但是当我第一次看到无数据错误并且不知何故它不起作用时,我尝试了这个解决方案,但也许是匆忙我犯了一个错误,现在似乎工作了,但我找到了一个我现在正在使用的函数,它将列表和要查找的值作为参数并返回位置然后我从列表中删除该项目,谢谢 【参考方案1】:
FOR i IN 1 .. ls_attr_list.COUNT LOOP

当您从集合中删除一个元素时会导致异常/错误。每次您删除一个元素时,它都会在集合中创建一个间隙,而下次您尝试运行该过程时,它将到达该间隙并抛出 ORA-01403: no data found

相反,您需要在i := ls_attr_list.FIRSTls_attr_list.LAST 之间循环,并使用i := ls_attr_list.NEXT(i) 来获取下一个索引。

一个简化的工作示例是:

CREATE TYPE VARCHAR2_TABLE AS TABLE OF VARCHAR2(20);
/
    
DECLARE
  vals VARCHAR2_Table := VARCHAR2_Table( 'a', 'b', 'c', 'd', 'e', 'f' );

  PROCEDURE del_Val (
    v IN OUT VARCHAR2_Table,
    x IN     VARCHAR2
  )
  AS
    idx     PLS_INTEGER;
  BEGIN
    IF v IS NULL OR v IS EMPTY THEN
      RETURN;
    END IF;
    idx := v.FIRST;
    LOOP
      EXIT WHEN idx IS NULL;
      IF v(idx) = x THEN
        DBMS_OUTPUT.PUT_LINE(idx);
        v.DELETE(idx);
      END IF;
      idx := v.NEXT(idx);
    END LOOP;
  END;

BEGIN
  del_Val( vals, 'b' );
  del_Val( vals, 'f' );
  del_Val( vals, 'a' );
END;
/

但是使用这个程序:

PROCEDURE del_Val (
  v IN OUT VARCHAR2_Table,
  x IN     VARCHAR2
)
AS
BEGIN
  FOR i IN 1 .. v.COUNT LOOP
    IF v(i) = x THEN
      DBMS_OUTPUT.PUT_LINE(i);
      v.DELETE(i);
    END IF;
  END LOOP;
END;

在第二次调用该过程时会导致ORA-01403: no data found

db小提琴here

【讨论】:

最明确的答案,标记为解决方案,谢谢 @AndreiMaieras 那是因为您不能使用FOR i IN t_tab.FIRST .. t_tab.LAST LOOP ... END LOOP; 来迭代稀疏集合。您需要使用i := t_tab.FIRST; LOOP ... EXIT WHEN i = t_tab.LAST; i := t_tab.NEXT(i); END LOOP; 进行迭代,否则您将收到异常,因为它将尝试访问稀疏集合中的间隙并失败。 用您的代码和问题发布另一个minimal reproducible example 问题。试图问它是 cmets 是不可读的。【参考方案2】:

我试图以一种非常清晰的方式复制该场景,希望这会有所帮助。

--Obj Creation
CREATE OR REPLACE type av_obj_test
IS
  object
  (
    col1 VARCHAR2(100),
    col2 VARCHAR2(100) );

--TABLE Type creation
CREATE OR REPLACE type av_ntt_test
IS
  TABLE OF av_obj_test;

--Anonymous block  
DECLARE
av_t av_ntt_test:=av_ntt_test(av_obj_test('av','roy'),av_obj_test('sh','roy'));
BEGIN
FOR I IN av_t.FIRST..av_t.LAST LOOP
IF av_t(i).col1 = 'av' THEN
av_t.DELETE(I);
END IF;
END LOOP;
END;

【讨论】:

这不是试图回答问题 - 它是问题的简化重新散列,应该对问题或评论进行编辑。【参考方案3】:

你没有做错任何事。当您删除某些内容时,会出现像@MT0 写的那样导致此错误的间隙。 我玩了一下你的代码,然后:

  declare
  t_tab T_ATTRIBUTEPAGE_ATTRIBUTELIST;
  i number;
  begin
t_tab:= T_ATTRIBUTEPAGE_ATTRIBUTELIST ();
t_tab.EXTEND(9);
t_tab(1):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('asd',1);
t_tab(2):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('tyh',2);
t_tab(3):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('adgdfsd',3);
t_tab(4):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('fg',4);
t_tab(5):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('test',5);
t_tab(6):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('test',6);
t_tab(7):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('adaaaasd',7);
t_tab(8):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('qwwwww',8);
t_tab(9):=O_ATTRIBUTEPAGE_ATTRIBUTELIST('poikj',9);
i:=t_tab.FIRST;
LOOP
 dbms_output.put_line(t_tab(i).AttributeName||' '||t_tab(i).AttrNUMBER);

 if t_tab(i).AttributeName = 'test' then t_tab.delete(i);
 t_tab(i):=null;
 end if;


 EXIT when i=t_tab.LAST;
 i:=t_tab.NEXT(i);
 end loop;

 dbms_output.put_line('==============================================');
  for i IN t_tab.FIRST..t_tab.LAST LOOP

  if  t_tab(i) is null then
 continue;
  else
 dbms_output.put_line(t_tab(i).AttributeName||' '||t_tab(i).AttrNUMBER);
  end if;
 end loop;
  end;
    /   

输出:

asd 1
tyh 2
adgdfsd 3
fg 4
test 5
test 6
adaaaasd 7
qwwwww 8
poikj 9
==============================================
asd 1
tyh 2
adgdfsd 3
fg 4
adaaaasd 7
qwwwww 8
poikj 9

t_tab.delete(i) a 给t_tab(i):=null; 之后消除了间隙,并且编译器没有任何问题可以再次遍历此集合并打印所有项目(因为没有间隙,所以有一些东西 - NULL,而 NULL 是一些东西,希望你会明白:))。和上面的用户一样 - 你应该使用 FIRST..LAST 而不是 count。

【讨论】:

在您的示例中,t_tab.delete(i); 完全是多余的,可以删除。您正在调用 t_tab.delete(i);,然后您立即使用 t_tab(i):=null; 重新创建元素 - 一个不存在的元素和一个 null 元素是两个完全不同的东西。 不是我写的吗? GAP(不存在的元素)和 null 是不同的东西? 正如我所说,如果您从示例中删除 t_tab.delete(i); 语句,代码将执行完全相同的操作。您(实际上)不是在删除,而是用 NULL 替换,如果 OP 依赖于 t_tab.COUNT 之类的东西(包括 NULL 元素但不删除元素)来了解集合。它还会使所有逻辑复杂化,因为您现在必须测试集合中的每个对象是否非空。 不,我是说您应该将任何内容设置为NULL,但您应该使用t_tab.DELETE(i);,然后使用FIRST/LAST/NEXT 正确迭代稀疏集合 - 设置NULL 的事情只是使用临时粘贴石膏创造更多的工作,然后你需要解决它。 那是因为您不能使用FOR i IN t_tab.FIRST .. t_tab.LAST LOOP ... END LOOP; 来遍历稀疏集合。你需要使用i := t_tab.FIRST; LOOP ... EXIT WHEN i = t_tab.LAST; i := t_tab.NEXT(i); END LOOP; 否则你会得到异常,因为它会尝试访问稀疏集合中的间隙并失败。【参考方案4】:

即使集合的最后一个元素被删除,下面也会处理

DECLARE
  TYPE VARCHAR2_TABLE IS TABLE OF VARCHAR2(20);
  vals VARCHAR2_Table := VARCHAR2_Table( 'a', 'b', 'c', 'd', 'e', 'f' );
  PROCEDURE del_Val (
    v IN OUT VARCHAR2_Table,
    x IN     VARCHAR2
  )
  AS
    i INT;
    v_last_deleted boolean;
  BEGIN
    IF v IS NULL OR v IS EMPTY THEN
      DBMS_OUTPUT.PUT_LINE('No more data');
      RETURN;
    END IF;
    i := v.FIRST;
    LOOP
      IF v(i) = x THEN
        DBMS_OUTPUT.PUT_LINE('  -> Delting element at : '||i||' that has vaulue of :'||v(i));
        if (i = v.LAST) then v_last_deleted := true; end if;
        v.DELETE(i);
      ELSE 
        DBMS_OUTPUT.PUT_LINE('  --> Not Delting element at : '||i||' that has vaulue of :'||v(i));      
      END IF;
      EXIT WHEN v IS NULL OR v IS EMPTY OR v_last_deleted OR (i = v.LAST);
      i := v.NEXT(i);
    END LOOP;
  END;
BEGIN
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'b' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'e' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'a' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'a' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'f' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'c' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'd' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'a' );
  DBMS_OUTPUT.PUT_LINE('Total Count : '||vals.count);
  del_Val( vals, 'b' );
END;
/

【讨论】:

以上是关于PL-SQL 从对象表中删除项目的主要内容,如果未能解决你的问题,请参考以下文章

使用 jquery 表中的复选框从数组中删除 JavaScript 对象

如何将日期对象作为第一个也是唯一的输入参数传递给 PL-SQL

使用jquery表中的复选框从数组中删除JavaScript对象

关于使用啥 pl-sql 从 Oracle 10G 数据库中提取大量记录(六百万)的任何建议,并从多个表中进行连接? [复制]

spring thymeleaf - 从 html 表中删除对象并将 id 传递给控制器

如何从java中的链表中删除一个对象?