Oracle FORALL和sql%rowcount(当FORALL从未输入时)

Oracle FORALL和sql%rowcount(当FORALL从未输入时),oracle,plsql,bulk-operations,Oracle,Plsql,Bulk Operations,我很好奇你们是如何处理这样一个问题的:sql%rowcount在一个根本没有输入的FORALL之后没有设置。下面我如何解决它的示例(使用变量v_rowcount和FORALL所基于的集合的count)。但我觉得有一种更聪明的方法: create table tst (id number); -- we start with an empty table declare type type_numbers is table of number; v_numbers type_nu

我很好奇你们是如何处理这样一个问题的:
sql%rowcount
在一个根本没有输入的
FORALL
之后没有设置。下面我如何解决它的示例(使用变量
v_rowcount
FORALL
所基于的集合的
count
)。但我觉得有一种更聪明的方法:

create table tst (id number); -- we start with an empty table

declare
    type type_numbers is table of number;
    v_numbers type_numbers;
    v_rowcount number;
begin

insert into tst values (1);                     
DBMS_OUTPUT.put_line(sql%rowcount); -- prints 1 which is correct, 1 row inserted

delete from tst;
DBMS_OUTPUT.put_line(sql%rowcount); -- prints 1 which is correct, 1 row deleted 

v_numbers := type_numbers(3,4,5);
forall v in 1 .. v_numbers.count
    update tst
    set id = v_numbers(v)
    where id = v_numbers(v);
DBMS_OUTPUT.put_line(sql%rowcount); -- prints 0 which is correct, 0 rows updated

insert into tst values (1);
DBMS_OUTPUT.put_line(sql%rowcount); -- prints 1 which is correct, 1 row inserted

delete from tst;
DBMS_OUTPUT.put_line(sql%rowcount); -- prints 1 which is correct, 1 row deleted  

v_numbers := type_numbers();
forall v in 1 .. v_numbers.count
    update tst
    set id = v_numbers(v)
    where id = v_numbers(v);
DBMS_OUTPUT.put_line(sql%rowcount); -- prints 1 which is WRONG, 0 rows updated (this is still the sql%rowcount of the DELETE above)

forall v in 1 .. v_numbers.count
    update tst
    set id = v_numbers(v)
    where id = v_numbers(v);
v_rowcount := 0;
if v_numbers.count > 0 then
    v_rowcount := sql%rowcount;
end if;
DBMS_OUTPUT.put_line(v_rowcount);   -- prints 0 which is correct, 0 rows updated

end;
/

SQL%ROWCOUNT与FORALL一起使用

请参见下面的示例:

-- Data preparation

CREATE TABLE EMPLOYEES (
    EMPID     NUMBER,
    EMPNAME   VARCHAR2(100)
);

INSERT INTO EMPLOYEES VALUES (
    1,
    'a'
);

INSERT INTO EMPLOYEES VALUES (
    2,
    'b'
);

INSERT INTO EMPLOYEES VALUES (
    3,
    'c'
);

INSERT INTO EMPLOYEES VALUES (
    4,
    'a'
);

INSERT INTO EMPLOYEES VALUES (
    5,
    'e'
);

SELECT * FROM EMPLOYEES;

因此,根据我的示例,它存储了SQL%ROWCOUNT中受影响的许多行,即使我们使用FORALL。---但是,如果我在“DELETE”语句之后删除“COMMIT”,那么我也将面临与您描述的相同的问题

因此,问题的解决方案是
COMMIT
语句。尝试在delete之后使用commit语句运行代码

希望这对你有用

已更新

BEGIN
    OPEN C_DATA;
    FETCH C_DATA BULK COLLECT INTO L_TAB;
    FORALL I IN 1..L_TAB.COUNT
        UPDATE EMPLOYEES
        SET
            EMPNAME = L_TAB(I).EMPNAME || L_TAB(I).EMPNAME
        WHERE
            EMPID = L_TAB(I).EMPID;

    DBMS_OUTPUT.PUT_LINE('1) SQL%ROWCOUNT IS ' || SQL%ROWCOUNT);
    CLOSE C_DATA;

    DELETE FROM EMPLOYEES;
    DBMS_OUTPUT.PUT_LINE('2) SQL%ROWCOUNT IS ' || SQL%ROWCOUNT);
    SAVEPOINT A; --- IMP

    OPEN C_DATA;
    FETCH C_DATA BULK COLLECT INTO L_TAB;
    FORALL I IN 1..L_TAB.COUNT
        UPDATE EMPLOYEES
        SET
            EMPNAME = L_TAB(I).EMPNAME || L_TAB(I).EMPNAME
        WHERE
            EMPID = L_TAB(I).EMPID;

    DBMS_OUTPUT.PUT_LINE('3) SQL%ROWCOUNT IS ' || SQL%ROWCOUNT);
    CLOSE C_DATA;
END;
/

我会像你的例子那样做,但也有其他选择。我们可以使用
sql%bulk\u rowcount
,这对于
forall
可能更好。下面是Oracle文档和一些示例。这是我的测试表:

create table test (id, val) as (
  select 1, 'PQR' from dual union all
  select 2, 'AB1' from dual union all
  select 2, 'AB2' from dual union all
  select 3, 'XYZ' from dual );
和示例代码块,其中我使用了短函数求和
bulk\u rowcount
s:

declare 
    type t is table of number;
    a t;

    function bulkcount(x in t) return number is 
      ret number := 0;
    begin
      for i in 1..x.count loop
        ret := ret + sql%bulk_rowcount(i);
      end loop;
      return ret;
    end bulkcount;
begin
    a := t(2, 3, 7);
    forall i in a.first..a.last 
        delete from test where id = a(i);

    dbms_output.put_line('sql rowcount: '||sql%rowcount);
    dbms_output.put_line('sum of bull_rowcount: '||bulkcount(a));

    a := t();
    forall i in a.first..a.last
        update test set val = 'ZZZ' where id = a(i);

    dbms_output.put_line('sql rowcount: '||sql%rowcount);
    dbms_output.put_line('sum of bull_rowcount: '||bulkcount(a));
end;
结果:

sql rowcount: 3
sum of bull_rowcount: 3
sql rowcount: 3          -- "wrong", value from previous DML
sum of bull_rowcount: 0

sql%rowcount
是上次dml的结果。未执行forall循环中的update,因此
sql%rowcount
显示delete语句的结果。怎么了?@hotfix谢谢。没问题。我只是好奇如何很好地处理这种情况。如果v_numbers.count>0,我用
的解决方案对我来说似乎很笨拙,甚至可能产生错误的结果(想不出一个例子)。我不太明白问题是什么。为什么不先检查集合是否为空?@hotfix这正是我的问题:-)这里的最佳做法是什么?在论坛之前检查收藏数量的最佳做法是什么?@hotfix-是的,EBKAC。嗨,谢谢。是sql%rowcount适用于FORALL,但仅当pl/sql集合不为空时。在您的示例中,l_选项卡包含5个对象。在我的示例中,集合不包含任何对象,因此未设置sql%rowcount。我的问题是如何在集合为空的FORALL之后处理sql%rowcount。看我的例子。泰。krI已根据您的要求更新了我的答案。请使用您的代码进行检查和测试。谢谢,但我不想仅为了sql%rowcount正确而提交:-)完美,我只是解释为什么它不工作。如果对你有用的话,我会给出解决方案。我已经更新了答案。只需使用
保存点
。无需
提交
。会有用的,谢谢!我知道你会和我的例子一样。和你一样,我也认为
bulk_rowcount
对于我的示例来说是一种过分的杀伤力,尤其是它与我的空集合示例所做的事情完全相同
ret number:=0;[…]对于1..x.count循环中的i[…]未输入循环…]结束循环;返回ret
对我来说等于我所做的
v_rowcount:=0;如果v_numbers.count>0,则[…未输入…]如果结束;DBMS_OUTPUT.put_行(v_rowcount)如果集合为空,则两个版本都返回数字0,而不检查任何sql%参数。是
bulk\u rowcount
是一个有趣的替代方案,它允许您为集合的每个元素查找受影响的行数,而
rowcount
forall
是不可能的。但在这里我会使用你的版本。
declare 
    type t is table of number;
    a t;

    function bulkcount(x in t) return number is 
      ret number := 0;
    begin
      for i in 1..x.count loop
        ret := ret + sql%bulk_rowcount(i);
      end loop;
      return ret;
    end bulkcount;
begin
    a := t(2, 3, 7);
    forall i in a.first..a.last 
        delete from test where id = a(i);

    dbms_output.put_line('sql rowcount: '||sql%rowcount);
    dbms_output.put_line('sum of bull_rowcount: '||bulkcount(a));

    a := t();
    forall i in a.first..a.last
        update test set val = 'ZZZ' where id = a(i);

    dbms_output.put_line('sql rowcount: '||sql%rowcount);
    dbms_output.put_line('sum of bull_rowcount: '||bulkcount(a));
end;
sql rowcount: 3
sum of bull_rowcount: 3
sql rowcount: 3          -- "wrong", value from previous DML
sum of bull_rowcount: 0