【问题标题】:Number of rows UPDATED and INSERTED from the same procedure in PL/SQLPL/SQL 中同一过程更新和插入的行数
【发布时间】:2020-09-14 16:07:05
【问题描述】:

我有一个程序执行INSERT INTO 然后是某些字段的UPDATE(都在同一个程序中),我正在使用@Clive Number of rows affected by an UPDATE in PL/SQL 的这个答案来了解具有的数据量已更新以放入日志,但它给我带来了总行数,而不仅仅是已更新的记录。

这是正确的认识方式吗?

我需要知道从INSERT STATEMENT 插入了多少行,从UPDATE STATEMENT 更新了多少行。

我的查询:

CREATE OR REPLACE PROCEDURE OWNER.TABLE_NAME
AS

 -- VARIABLE
 v_qtd_regs  number := 0;  
 v_code      number;
 v_errm      VARCHAR2(500);
 start_time  pls_integer;
 end_time    pls_integer;
 elapse_time number;
 proc_name   varchar2(100);
 i NUMBER;

BEGIN
   proc_name := 'PRDWBI_CGA_D_COLUMNS';
   start_time := dbms_utility.get_time;
   DS_FUNCESP.PRDSBI_GRAVA_LOG( 'I', 'DataWarehouse', proc_name, 'Início Carga' );

   -- INSERT INTO TABLE:
   INSERT INTO OWNER.TABLE_NAME
   (COLUMN_ID, COLUMNS_NAME, COLUMN_NAME2)
   (SELECT 1 AS COLUMN_ID, 'TEST' AS COLUMN_NAME, SYSDATE AS COLUMN_NAME2 FROM DUAL);

   COMMIT;

  -- UPDATE SOME COLUMNS I NEED
   UPDATE OWNER.TABLE_NAME y
   SET (y.COLUMNS_NAME, y.COLUMN_NAME2) = 
               (SELECT 'TEST2' AS COLUMN_NAME, SYSDATE AS COLUMN_NAME2 FROM DUAL x WHERE x.COLUMN_ID = y.COLUMN_ID)
   WHERE EXISTS (SELECT 'TEST2' AS COLUMN_NAME, SYSDATE AS COLUMN_NAME2 FROM DUAL x WHERE x.COLUMN_ID = y.COLUMN_ID);

   -- TO KNOW HOW MANY ROWS WERE UPDATED
   i := SQL%rowcount;     

   COMMIT;  

   --dbms_output.Put_line(i);

   SELECT COUNT(1) INTO v_qtd_regs FROM OWNER.TABLE_NAME where LinData >= TRUNC(SYSDATE);
   end_time := dbms_utility.get_time;
   elapse_time := ((end_time - start_time)/100);
   v_errm := SUBSTR(SQLERRM, 1 , 500);
   DS_FUNCESP.PRDSBI_GRAVA_LOG('T', 'DataWarehouse', proc_name, v_errm, v_qtd_regs, elapse_time );

   COMMIT;

EXCEPTION
   WHEN OTHERS THEN
      v_code := SQLCODE;
      v_errm := SUBSTR(SQLERRM, 1 , 500);
      DS_FUNCESP.PRDSBI_GRAVA_LOG('E', 'Error', proc_name, v_errm);

END;

为展示真实示例而编辑的问题:

我创建了一个从“SYS.DBA_TAB_COLUMNS”获取数据的表,仅用作示例,如下所示:

  CREATE TABLE "DW_FUNCESP"."D_TEST"
    (
        "ID_COLUMN" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1
        START WITH 1 CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE,
        "NM_OWNER"  VARCHAR2(500 CHAR) NOT NULL ENABLE                       ,
        "NM_TABLE"  VARCHAR2(500 CHAR) NOT NULL ENABLE                       ,
        "CD_COLUMN" NUMBER(20,0) NOT NULL ENABLE                             ,
        "NM_COLUMN" VARCHAR2(500 CHAR) NOT NULL ENABLE                       ,
        "DS_COLUMN" VARCHAR2(500 CHAR) NOT NULL ENABLE                       ,
        "LINDATE"   DATE DEFAULT SYSDATE NOT NULL ENABLE                     ,
        "LINORIGIN" VARCHAR2(100 CHAR) NOT NULL ENABLE
    )

然后我创建了一个过程来识别插入和更新的记录,如下所示:

CREATE OR REPLACE PROCEDURE DW_FUNCESP.PRDWBI_CGA_D_TEST
AS

 -- variaveis de suporte as informações que deve gravar
 rows_inserted integer;
 rows_updated  integer;

BEGIN

   -- Insert Into table
   INSERT INTO DW_Funcesp.D_TEST
   (NM_OWNER, NM_TABLE, CD_COLUMN, NM_COLUMN, DS_COLUMN, LINDATE, LINORIGIN)
   (SELECT
      NVL(x.NM_OWNER ,'NOT FOUND')      AS NM_OWNER     ,
      NVL(x.NM_TABLE ,'NOT FOUND')      AS NM_TABLE     ,
      NVL(x.CD_COLUMN ,-1)              AS CD_COLUMN    ,
      NVL(x.NM_COLUMN ,'NOT FOUND')     AS NM_COLUMN    ,
      NVL(x.DS_COLUMN ,x.NM_COLUMN)     AS DS_COLUMN    ,
      SYSDATE                           AS LINDATE      ,
      'SYS.DBA_TAB_COLUMNS'             AS LINORIGIN
    FROM
      (
        SELECT
          d.OWNER       AS NM_OWNER ,
          d.TABLE_NAME  AS NM_TABLE ,
          d.COLUMN_ID   AS CD_COLUMN,
          d.COLUMN_NAME AS NM_COLUMN,
          e.COMMENTS    AS DS_COLUMN
        FROM SYS.DBA_TAB_COLUMNS d
        LEFT JOIN SYS.DBA_COL_COMMENTS e
          ON e.OWNER           = d.OWNER
          AND e.TABLE_NAME  = d.TABLE_NAME
          AND e.COLUMN_NAME = d.COLUMN_NAME
        WHERE d.OWNER = 'DW_FUNCESP'
       ) x
    LEFT JOIN DW_FUNCESP.D_TEST y
      ON  y.NM_OWNER = x.NM_OWNER
      AND y.NM_TABLE = x.NM_TABLE
      AND y.NM_COLUMN = x.NM_COLUMN
    WHERE y.ID_COLUMN IS NULL);

    rows_inserted := sql%rowcount;

  -- Update the table
   UPDATE DW_FUNCESP.D_TEST z
   SET (z.NM_COLUMN, z.DS_COLUMN, z.LINDATE) = 
                  (SELECT
                      NVL(x.NM_COLUMN ,'NOT FOUND')     AS NM_COLUMN    ,
                      NVL(x.DS_COLUMN ,x.NM_COLUMN)     AS DS_COLUMN    ,
                      SYSDATE                           AS LINDATE      
                    FROM
                      (
                        SELECT
                          d.OWNER       AS NM_OWNER ,
                          d.TABLE_NAME  AS NM_TABLE ,
                          d.COLUMN_ID   AS CD_COLUMN,
                          d.COLUMN_NAME AS NM_COLUMN,
                          e.COMMENTS    AS DS_COLUMN
                        FROM SYS.DBA_TAB_COLUMNS d
                        LEFT JOIN SYS.DBA_COL_COMMENTS e
                          ON e.OWNER           = d.OWNER
                          AND e.TABLE_NAME  = d.TABLE_NAME
                          AND e.COLUMN_NAME = d.COLUMN_NAME
                        WHERE d.OWNER = 'DW_FUNCESP'
                       ) x
                    WHERE z.NM_OWNER = x.NM_OWNER
                      AND z.NM_TABLE = x.NM_TABLE
                      AND z.CD_COLUMN = x.CD_COLUMN)
   WHERE EXISTS (SELECT
                      NVL(x.NM_COLUMN ,'NOT FOUND')     AS NM_COLUMN    ,
                      NVL(x.DS_COLUMN ,x.NM_COLUMN)     AS DS_COLUMN    ,
                      SYSDATE                           AS LINDATE      
                    FROM
                      (
                        SELECT
                          d.OWNER       AS NM_OWNER ,
                          d.TABLE_NAME  AS NM_TABLE ,
                          d.COLUMN_ID   AS CD_COLUMN,
                          d.COLUMN_NAME AS NM_COLUMN,
                          e.COMMENTS    AS DS_COLUMN
                        FROM SYS.DBA_TAB_COLUMNS d
                        LEFT JOIN SYS.DBA_COL_COMMENTS e
                          ON e.OWNER           = d.OWNER
                          AND e.TABLE_NAME  = d.TABLE_NAME
                          AND e.COLUMN_NAME = d.COLUMN_NAME
                        WHERE d.OWNER = 'DW_FUNCESP'
                       ) x
                    WHERE z.NM_OWNER = x.NM_OWNER
                      AND z.NM_TABLE = x.NM_TABLE
                      AND z.CD_COLUMN = x.CD_COLUMN);

   rows_updated := sql%rowcount; 

   dbms_output.Put_line('inserted=>' || to_char(rows_inserted) || ', updated=>' || to_char(rows_updated));

   COMMIT;

EXCEPTION
   WHEN OTHERS THEN
      RAISE;

END;

所以我的第一个插入输出是:

插入=>2821,更新=>2821

所以我选择了要更改的数据并对其进行了更新,我进行了以下选择以选择应更新哪些数据以再次引入 DBMS 输出:

SELECT * FROM DW_FUNCESP.D_TEST WHERE NM_TABLE = 'D_TEST';

我在如图所示的列中发表了评论,以带来更新:

COMMENT ON COLUMN DW_FUNCESP.D_TEST.LINORIGIN IS 'The origin of the data';

我再次运行程序,输出为:

插入=>0,更新=>2821

该更新的结果:

您不应该只在输出中带来 1 个更新的数据,因为只有 1 个更新?而不是所有的行?

例如:插入=>0,更新=>1

所以我的问题仍然存在,我问对了吗?是否有可能在相同的程序中获得此结果?是不是更新不正确(尽管已经更新了数据)?

【问题讨论】:

    标签: oracle plsql oracle-sqldeveloper


    【解决方案1】:

    您没有得到插入的行和更新的行。 SQL%rowcount 仅包含最后一个选择或 DML 语句中的行数。由于您仅在更新后设置变量,因此您只能获得更新次数。如果你想要两者,那么你需要一个单独的变量。
    提示:没有必要在每个 DML 之后提交,实际上这通常被认为是一种非常糟糕的做法。你需要研究一下交易。基本思想是所有操作都成功完成或没有一个成功完成。查找 ATOMIC 和 Atomicity

    所以你修改后的程序变成:

    create or replace procedure owner.table_name
    as
    
     -- VARIABLE
     v_qtd_regs  number := 0;  
     v_code      number;
     v_errm      varchar2(500);
     start_time  pls_integer;
     end_time    pls_integer;
     elapse_time number;
     proc_name   varchar2(100);
     rows_inserted integer;
     rows_updated  integer;
    begin
       proc_name := 'PRDWBI_CGA_D_COLUMNS';
       start_time := dbms_utility.get_time;
       ds_funcesp.prdsbi_grava_log( 'I', 'DataWarehouse', proc_name, 'Início Carga' );
    
    
       insert into owner.table_name
       (column_id, columns_name, column_name2)
       (select 1 as column_id, 'TEST' as column_name, sysdate as column_name2 from dual);
    
       rows_inserted := sql%rowcount;
    
       update owner.table_name y
       set (y.columns_name, y.column_name2) = 
                   (select 'TEST2' as column_name, sysdate as column_name2 from dual x where x.column_id = y.column_id)
       where exists (select 'TEST2' as column_name, sysdate as column_name2 from dual x where x.column_id = y.column_id);
    
       rows_updated := sql%rowcount;     
    
    
       dbms_output.Put_line('inserted=>' || to_char(rows_inserted) || ', updated=>' || tp_char(rows_updated));
    
       select count(1) into v_qtd_regs from owner.table_name where lindata >= trunc(sysdate);
       end_time := dbms_utility.get_time;
       elapse_time := ((end_time - start_time)/100);
       v_errm := substr(sqlerrm, 1 , 500);
       ds_funcesp.prdsbi_grava_log('T', 'DataWarehouse', proc_name, v_errm, v_qtd_regs, elapse_time );
    
       commit;
    
    exception
       when others then
          v_code := sqlcode;
          v_errm := substr(sqlerrm, 1 , 500);
          ds_funcesp.prdsbi_grava_log('E', 'Error', proc_name, v_errm);
    
    end;
    

    【讨论】:

    • 是的,我正在努力获得最佳实践,还有很多东西要学!但结果是一样的。它从上面的查询返回 inserted=>0, updated=>4582。是更新吗?它正在更新这些列的所有行?
    • 您需要以文本形式发布示例数据 - 无图片。此数据应包括应更新和不应更新的两行。然后展示你实际得到的结果以及你期望得到的结果。还发布表定义。更好的fiddle 用于表定义和示例数据..
    • 我把问题改成一个真实的例子,所以我创建了一个表格,一个过程,并一步一步地执行它来显示每个的输出。我在这里真的尽力了。
    【解决方案2】:

    尝试在每个DML后添加指令i := SQL%rowcount;

    1. 在 INSERT 之后获得插入的行数
    2. 在 UPDATE 之后获得更新的行数

    【讨论】:

    • 我在每个之后都添加了,但它们都返回了 0。 Dbms_output.Put_line(i); = 0。但是,根据样本,一些记录已更新。
    • 如果我在每个COMMIT之前添加,更新的指令i := SQL%rowcount;返回所有行的总数,看起来每一行都更新了。
    • 抱歉,我无法解释为什么它确实像docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/…中记录的那样工作
    • 抱歉我的错字,我的意思是:我无法解释为什么它按记录工作。
    【解决方案3】:

    我会使用 ORA_SCN 作为其他答案的建议,如果您有兴趣 什么 行已被插入或更新。但是您只想知道有多少,所以我会将计数留给 Oracle(对于大量行可能会很耗时)。

    请查看数据字典视图USER_TAB_MODIFICATIONS(或ALL_TAB_MODIFICATIONS,如果表位于过程之外的另一个架构中。

    CREATE TABLE d (
      id  NUMBER GENERATED ALWAYS AS IDENTITY,
      dt  DATE DEFAULT SYSDATE,
      foo VARCHAR2(128 BYTE)
    );
    

    收集表统计信息将重置修改视图:

    EXEC DBMS_STATS.GATHER_TABLE_STATS(NULL,'D');
    

    现在在您的 INSERT 之后,修改视图将包含插入的行数:

    INSERT INTO d(foo) SELECT object_name FROM all_objects;
    67,141 rows inserted.
    
    SELECT inserts, updates, deletes FROM user_tab_modifications WHERE table_name='D';
    INSERTS UPDATES DELETES
      67141       0       0
    

    同样,在 UPDATE 之后,更新的行:

    UPDATE d SET foo=lower(foo),dt=SYSDATE WHERE mod(id,10)=0; 
    6,714 rows updated.
    
    SELECT inserts, updates, deletes FROM user_tab_modifications WHERE table_name='D';
    INSERTS UPDATES DELETES
      67141    6714       0
    

    为了清楚起见,我使用了 SQL 而不是 PL/SQL。您可能必须向包含该过程的模式授予一些特殊权限。如果您遇到问题,请用我的名字添加评论。

    【讨论】:

      猜你喜欢
      • 2016-07-30
      • 2012-05-23
      • 1970-01-01
      • 2021-07-07
      • 2017-10-18
      • 1970-01-01
      • 1970-01-01
      • 2020-07-07
      相关资源
      最近更新 更多