我的长时间 SQL*Plus 循环在执行期间不打印 DBMS_OUTPUT.PUT_LINE 输出

My long time SQL*Plus loop doesn't print DBMS_OUTPUT.PUT_LINE output during execution

我知道为了在 sqlplus 上打印如下内容:

begin
   dbms_output.put_line('Hello!'); 
end;
/

我需要打电话

set serveroutput on;

在那之前。 我也知道不需要,但我也可以调用

DBMS_OUTPUT.enable;

之前,以防万一。这对我有用。

但是如果我想一直打印一个长循环的进度怎么办?这对我来说似乎是不可能的。我已经尝试了一切以在下面的循环中打印一些进度,但就是行不通。有什么办法吗?我什至尝试假脱机到一个文件,但没有成功。

注意 1:我无法截断或分区这个 table 因为 DBA 不想帮助我,所以我不得不使用这个讨厌的循环...

注意 2:我注意到一旦循环完成,就会打印出整个输出。看起来 oracle 正在缓冲输出并在最后打印所有内容。我不确定如何避免这种情况并在每次循环迭代时打印。

set serveroutput on;
declare
    e number;
    i number;
    nCount number;
    f number;
begin
    DBMS_OUTPUT.enable;
    dbms_output.put_line('Hello!'); 
    select count(*) into e from my_big_table  where upd_dt < to_date(sysdate-64);
    f :=trunc(e/10000)+1;
    for i in 1..f
    loop
       delete from my_big_table where upd_dt < to_date(sysdate-64) and rownum<=10000;
       commit;
       DBMS_OUTPUT.PUT_LINE('Progress: ' || to_char(i) || ' out of ' || to_char(f));
    end loop;
end;

感谢您的回答。

DBMS_OUTPUT 只会在 PL/SQL 代码终止并且控制返回给调用程序后才会显示。

如您所见,输出已缓冲。当您的 PL/SQL 代码完成时,调用程序(例如 SQL*Plus)可以去获取该输出。

插入另一个 table,也许称之为“MYOUTPUT”。

创建 table:

create table myoutput (lineno number, outline varchar2(80));

删除后添加:

insert into MYOUTPUT values (i,'Progress: ' || to_char(i) || ' out of ' || to_char(f));

然后 select 从 MYOUTPUT 定期查看进度。

select outline from myoutput order by lineno;

鲍比

您可以使用 UTL_FILE 将输出写入外部文件,如:

DECLARE
  fh          UTL_FILE.FILE_TYPE;
  nRow_count  NUMBER := 0;
BEGIN
  fh := UTL_FILE.FOPEN('DIRECTORY_NAME', 'some_file.txt', 'w');

  FOR aRow IN (SELECT *
                 FROM SOME_TABLE)
  LOOP
    nRow_count := nRow_count + 1;

    IF nRow_count MOD 1000 = 0 THEN
      UTL_FILE.PUT_LINE(fh, 'Processing row ' || nRow_count);
      UTL_FILE.FFLUSH(fh);
    END IF;

    -- Do something useful with the data in aRow
  END LOOP;  -- aRow

  UTL_FILE.FCLOSE_ALL;  -- Close all open file handles, including
                        -- the ones I've forgotten about...
END;

此类事情有两种标准方法:

  1. 在您的会话中设置模块和操作 DBMS_APPLICATION_INFO.SET_MODULE:

    SQL> exec DBMS_APPLICATION_INFO.SET_MODULE('my_long_process', '1 from 100');
    
    PL/SQL procedure successfully completed.
    
    SQL> select action from v$session where module='my_long_process';
    
    ACTION
    ----------------------------------------------------------------
    1 from 100
    
  2. 设置session_longops: DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS

    我建议您使用它,因为它专为长时间操作而设计。
    Example on Oracle-Base.

----

PS: dbms_output,put_line 将所有输出保存在 dbms_output 包的集合(嵌套 table)变量中,所以你不能从另一个会话获取它,客户端在用户调用(执行)期间无法获取它。除了 set serveroutput on 之外,您还可以使用 dbms_output.get_lines 获取输出:http://orasql.org/2017/12/10/sqlplus-tips-8-dbms_output-without-serveroutput-on/

顺便说一句,如果您需要过滤或分析 dbms_output 的输出,有时在查询中获取输出很方便,因此您可以在 where 子句中使用过滤字符串或聚合它们:https://gist.github.com/xtender/aa12b537d3884f4ba82eb37db1c93c25