实时 PL/SQL 输出

发布于 2024-08-06 13:35:31 字数 135 浏览 6 评论 0原文

是否可以实时获得 PL/SQL 的输出?我有一个相当大的包裹,运行了一个多小时,我想看看该包裹在特定时间在哪里。

无论如何,我目前使用一个日志表来执行此操作,每次运行该表都会填充数百个日志描述,我只是好奇这是否可能。

谢谢!

Is it possible to have Outputs from PL/SQL in real time? I have a pretty huge package that runs for more than an hour and I'd like to see where the package is at a particular time.

Anyways, I currently do this with a log table, which gets filled up with hundreds of log descriptions per run, I'm just curious if this is possible.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

可可 2024-08-13 13:35:31

这就是我使用的东西(输出可以在 v$session 和 v$session_longops 中看到)...

DECLARE
   lv_module_name   VARCHAR2(48);
   lv_action_name   VARCHAR2(32);

   gc_MODULE   CONSTANT   VARCHAR2(48) := 'MY_PROC';

   -- For LONGOPS
   lv_rindex BINARY_INTEGER;
   lv_slno   BINARY_INTEGER;

   lc_OP_NAME   CONSTANT   VARCHAR2(64)   :=   '['||gc_MODULE||']';
   lv_sofar   NUMBER;

   -- This is a guess as to the amount of work we will do
   lv_totalwork   NUMBER;
   lc_TARGET_DESC   CONSTANT VARCHAR2(64) := 'Tables';
   lc_UNITS   CONSTANT VARCHAR2(64) := 'Rows';

   CURSOR tab_cur
   IS
      SELECT owner, table_name
        FROM all_tables;

BEGIN
   <<initialisation>>
   BEGIN
      -- To preserve the calling stack, read the current module and action
      DBMS_APPLICATION_INFO.READ_MODULE( module_name => lv_module_name
                                       , action_name => lv_action_name );

      -- Set our current module and action
      DBMS_APPLICATION_INFO.SET_MODULE( module_name => gc_MODULE
                                      , action_name => NULL );
   END initialisation;

   <<main>>
   BEGIN
      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 01' );
      NULL;

      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 02' );
      FOR tab_rec IN tab_cur
      LOOP
         DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );
         NULL;
      END LOOP;

      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 03' );

      --Initialising longops
      lv_rindex := DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS_NOHINT;
      lv_sofar := 0;
      lv_totalwork := 5000; -- This is a guess, but could be actual if the query is quick

      FOR tab_rec IN tab_cur
      LOOP
         DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );

         lv_sofar := lv_sofar + 1;

         -- Update our totalwork guess
         IF lv_sofar > lv_totalwork
         THEN
            lv_totalwork := lv_totalwork + 500;
         END IF;

         DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex      => lv_rindex
                                                  , slno        => lv_slno
                                                  , op_name     => lc_OP_NAME
                                                  , sofar       => lv_sofar
                                                  , totalwork   => lv_totalwork
                                                  , target_desc => lc_TARGET_DESC
                                                  , units       => lc_UNITS
                                                  );
      END LOOP;

      -- Clean up longops
      DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex      => lv_rindex
                                               , slno        => lv_slno
                                               , op_name     => lc_OP_NAME
                                               , sofar       => lv_sofar
                                               , totalwork   => lv_sofar
                                               , target_desc => lc_TARGET_DESC
                                               , units       => lc_UNITS
                                               );
   END main;

   <<finalisation>>
   BEGIN
      -- Reset the module and action to the values that may have called us
      DBMS_APPLICATION_INFO.SET_MODULE( module_name => lv_module_name
                                      , action_name => lv_action_name );

      -- Clear the client info, preventing any inter process confusion for anyone looking at it
      DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => NULL );
   END finalisation;
END;
/

This is the kind of thing I use (output can be seen in v$session and v$session_longops)...

DECLARE
   lv_module_name   VARCHAR2(48);
   lv_action_name   VARCHAR2(32);

   gc_MODULE   CONSTANT   VARCHAR2(48) := 'MY_PROC';

   -- For LONGOPS
   lv_rindex BINARY_INTEGER;
   lv_slno   BINARY_INTEGER;

   lc_OP_NAME   CONSTANT   VARCHAR2(64)   :=   '['||gc_MODULE||']';
   lv_sofar   NUMBER;

   -- This is a guess as to the amount of work we will do
   lv_totalwork   NUMBER;
   lc_TARGET_DESC   CONSTANT VARCHAR2(64) := 'Tables';
   lc_UNITS   CONSTANT VARCHAR2(64) := 'Rows';

   CURSOR tab_cur
   IS
      SELECT owner, table_name
        FROM all_tables;

BEGIN
   <<initialisation>>
   BEGIN
      -- To preserve the calling stack, read the current module and action
      DBMS_APPLICATION_INFO.READ_MODULE( module_name => lv_module_name
                                       , action_name => lv_action_name );

      -- Set our current module and action
      DBMS_APPLICATION_INFO.SET_MODULE( module_name => gc_MODULE
                                      , action_name => NULL );
   END initialisation;

   <<main>>
   BEGIN
      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 01' );
      NULL;

      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 02' );
      FOR tab_rec IN tab_cur
      LOOP
         DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );
         NULL;
      END LOOP;

      DBMS_APPLICATION_INFO.SET_ACTION( action_name => 'Part 03' );

      --Initialising longops
      lv_rindex := DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS_NOHINT;
      lv_sofar := 0;
      lv_totalwork := 5000; -- This is a guess, but could be actual if the query is quick

      FOR tab_rec IN tab_cur
      LOOP
         DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => 'Rows = ['||TO_CHAR( tab_cur%ROWCOUNT, '999,999,999' )||']' );

         lv_sofar := lv_sofar + 1;

         -- Update our totalwork guess
         IF lv_sofar > lv_totalwork
         THEN
            lv_totalwork := lv_totalwork + 500;
         END IF;

         DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex      => lv_rindex
                                                  , slno        => lv_slno
                                                  , op_name     => lc_OP_NAME
                                                  , sofar       => lv_sofar
                                                  , totalwork   => lv_totalwork
                                                  , target_desc => lc_TARGET_DESC
                                                  , units       => lc_UNITS
                                                  );
      END LOOP;

      -- Clean up longops
      DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS( rindex      => lv_rindex
                                               , slno        => lv_slno
                                               , op_name     => lc_OP_NAME
                                               , sofar       => lv_sofar
                                               , totalwork   => lv_sofar
                                               , target_desc => lc_TARGET_DESC
                                               , units       => lc_UNITS
                                               );
   END main;

   <<finalisation>>
   BEGIN
      -- Reset the module and action to the values that may have called us
      DBMS_APPLICATION_INFO.SET_MODULE( module_name => lv_module_name
                                      , action_name => lv_action_name );

      -- Clear the client info, preventing any inter process confusion for anyone looking at it
      DBMS_APPLICATION_INFO.SET_CLIENT_INFO( client_info => NULL );
   END finalisation;
END;
/
幸福不弃 2024-08-13 13:35:31

我不知道这是否正是您想要的,但我使用 dbms_application_info.set_module 来查看我的包在哪里。

dbms_application_info.set_module(module_name => 'Conversion job',
                                 action_name => 'updating table_x'); 

v$session 的查询将显示该过程的哪一部分正在运行。

I don't know if this is exactly what you want but I use dbms_application_info.set_module to see where my package is.

dbms_application_info.set_module(module_name => 'Conversion job',
                                 action_name => 'updating table_x'); 

A query on v$session will show you which part of the procedure is running.

月亮是我掰弯的 2024-08-13 13:35:31

您可以使用 自主事务(例如这个SO中建议的)。

这将允许您在日志表中写入和提交,而无需提交主事务。然后,您将能够跟踪主脚本运行时发生的情况(顺便说一句,它还允许您计时/调整批处理)。

you could use autonomous transactions (as suggested in this SO for example).

This would allow you to write and commit in a log table without commiting the main transaction. You would then be able to follow what happens in your main script while it is running (incidentally, it will also allow you to time/tune your batch).

南城追梦 2024-08-13 13:35:31

使用 DBMS_PIPE 将消息写入命名管道。在另一个会话中,您可以从管道读取消息。非常简单,就像一个魅力!

procedure sendmessage(p_pipename varchar2
                        ,p_message  varchar2) is
      s number(15);
   begin
      begin
         sys.dbms_pipe.pack_message(p_message);
      exception
         when others then
            sys.dbms_pipe.reset_buffer;
      end;

      s := sys.dbms_pipe.send_message(p_pipename, 0);

      if s = 1
      then
         sys.dbms_pipe.purge(p_pipename);
      end if;
   end;




function receivemessage(p_pipename varchar2
                          ,p_timeout  integer) return varchar2 is
      n   number(15);
      chr varchar2(200);
   begin
      n := sys.dbms_pipe.receive_message(p_pipename, p_timeout);

      if n = 1
      then
         return null;
      end if;

      sys.dbms_pipe.unpack_message(chr);
      return(chr);
   end;

Use DBMS_PIPE to write a message to a named pipe. In another session you can read the messages from the pipe. Very simple, works like a charm !

procedure sendmessage(p_pipename varchar2
                        ,p_message  varchar2) is
      s number(15);
   begin
      begin
         sys.dbms_pipe.pack_message(p_message);
      exception
         when others then
            sys.dbms_pipe.reset_buffer;
      end;

      s := sys.dbms_pipe.send_message(p_pipename, 0);

      if s = 1
      then
         sys.dbms_pipe.purge(p_pipename);
      end if;
   end;




function receivemessage(p_pipename varchar2
                          ,p_timeout  integer) return varchar2 is
      n   number(15);
      chr varchar2(200);
   begin
      n := sys.dbms_pipe.receive_message(p_pipename, p_timeout);

      if n = 1
      then
         return null;
      end if;

      sys.dbms_pipe.unpack_message(chr);
      return(chr);
   end;
只是一片海 2024-08-13 13:35:31

如果您的长时间运行作业正在处理大量大小相当均匀的任务,您可能会发现会话 longops 是监视作业进度的好方法,并且允许您估计作业需要多长时间才能完成。

DBMS_APPLICATION_INFO.set_session_longops

If your long-running job is processing a large number of fairly evenly sized tasks, you may find session longops a good way of monitoring the job progress, as well as allowing you to estimate how long the job will take to finish.

DBMS_APPLICATION_INFO.set_session_longops

安静 2024-08-13 13:35:31

如果您可以从 PL/SQL 环境访问 shell,则可以调用 netcat:

BEGIN RUN_SHELL('echo "'||v_msg||'" | nc '||v_host||' '||v_port||' -w 5' );结尾;

/

v_host 是运行 python 脚本的主机,它从端口 v_port 上的套接字读取数据。

我在为 shell 和 pl/sql 编写 aplogr 时使用了这种设计日志监控。

If you have access to shell from PL/SQL environment you can call netcat:

BEGIN RUN_SHELL('echo "'||v_msg||'" | nc '||v_host||' '||v_port||' -w 5'); END;

/

v_host is a host running python script that reads data from socket on port v_port.

I used this design when I wrote aplogr for shell and pl/sql logs monitoring.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文