回滚过程创建文件

时间:2017-01-03 16:24:16

标签: oracle plsql export procedure

我有几个PL / SQL过程使用UTL_FILE导出文件中的表。

这是一个快照:

PROCEDURE export_t1
  AS
    l_file      UTL_FILE.FILE_TYPE;
    record       VARCHAR2(4096);
  BEGIN

    l_file := UTL_FILE.FOPEN(DIRECTORY_PATH, FILENAME, 'A');

    FOR j IN
    (SELECT * FROM PRODUCTS WHERE HANDLE = '0')
    LOOP
     l_record := j.id || ',' || j.code || ',' || j.desc ....... [others fields];
     UTL_FILE.PUT_LINE(l_file,l_record);
    END LOOP;

    UTL_FILE.FCLOSE(l_file);
    UPDATE PRODUCTS SET HANDLE = '1' WHERE HANDLE = '0';

  EXCEPTION
  WHEN OTHERS THEN
    -- log
   RAISE;
  END export_t1;

所以我有export_t1,export_t2,export_tn程序。另外我按顺序在'main'程序中调用它们。

我的问题是..如果我在export_t2中有一个例外,那是第二个 过程,如何阻止第一个(export_t1)创建文件

这个想法是..当那些ALL程序消失时创建文件OK,没有异常

3 个答案:

答案 0 :(得分:2)

除非您可以让您的文件系统参与两阶段提交(据我所知,目前不可能),协调文件输出与您的数据库事务将很困难,因为您的文件操作谎言在数据库事务范围之外。

即,总有一种理论情况,即在错误的时间发生某些事情,并且您的数据库和文件系统不同步。 (排序让你欣赏COMMIT为我们所做的一切。

无论如何,一个可能的策略是设计事物,以便出错的窗口尽可能短。如,

begin
  delete_real_files;  -- delete leftovers.
  write_temp_file_n1;
  write_temp_file_n2;
  write_temp_file_n3;
  ...
  write_temp_file_nx;

  rename_temp_files_to_real;

  commit;

  -- don't do anything else with the files after this point

exception
  when others then
    remove_real_files;
    remove_temp_files;
    rollback;
end;

这里的想法是将所有文件写入临时文件。如果出现故障,请将其清理干净。没有一个过程能够看到真实的"文件,因为你从未创建它们。只有在最后,您才能通过重命名临时文件。

这里的风险是你的前几个临时文件被成功重命名,但后续临时文件无法重命名AND(A)进程跳转并在异常处理程序删除它们之前看到它们或(B)异常处理程序因某些原因无法删除它们。

我喜欢这种方法,因为它将所有风险与重命名文件联系起来,这是一种非常安全的操作(因为它不需要额外的磁盘空间)。一些重命名不太可能成功,有些会失败。

这种方法有很多变化是可能的。但要记住的是,你并没有在这里实施坚如磐石的解决方案。总是有可能出现问题,因此根据您的容错程度,实施所需的任何检查(系统中的其他地方)。

答案 1 :(得分:0)

UTL_FILE.FCLOSE(或UTL_FILE.FFLUSH)字面写入磁盘。如果您不想写入磁盘,则不能写入磁盘 - 在将所有数据写入每个缓冲区之前,请不要关闭或刷新文件处理程序。

根据 n 的大小,您可能会有大量打开的文件处理程序,并且缓存了大量数据。这不会很漂亮。

最好创建另一个过程来调用UTL_FILE.FREMOVE,这会删除一个命名文件(假设有足够的权限)。

我会在Oracle scheduler中执行此操作,因为每个过程都是链中的一个单独步骤,您可以define a rule使用scheduler chain condition syntax调用过程来删除文件te链中的错误。

答案 2 :(得分:0)

迈克尔,

您可以使用 - > utl_file.fremove(目录路径,文件名);在异常中删除文件。

示例代码如下。

程序1:

CREATE OR REPLACE PROCEDURE SHAREFLE IS
v_MyFileHandle UTL_FILE.FILE_TYPE;
BEGIN
v_MyFileHandle := UTL_FILE.FOPEN('TEST_DIR','HELLO.TXT','a');
UTL_FILE.PUT_LINE(v_MyFileHandle, 'Hello Again for the Second Time! ' ||      
TO_CHAR(SYSDATE,'MM-DD-YY HH:MI:SS AM'));
UTL_FILE.FCLOSE(v_MyFileHandle);
SHAREFLE1;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE
('ERROR ' || TO_CHAR(SQLCODE) || SQLERRM);
NULL; 
END; 

程序2:

 CREATE OR REPLACE PROCEDURE SHAREFLE1 IS
 v_MyFileHandle UTL_FILE.FILE_TYPE;
 BEGIN
 v_MyFileHandle := UTL_FILE.FOPEN('TEST_DIR','HELLO.TXT','a');
   UTL_FILE.PUT_LINE(v_MyFileHandle, 'Hello Again for the Third Time! ' ||    TO_CHAR(SYSDATE,'MM-DD-YY HH:MI:SS AM'));
   UTL_FILE.FCLOSE(v_MyFileHandle);
   EXCEPTION
   WHEN OTHERS THEN
   DBMS_OUTPUT.PUT_LINE
   ('ERROR ' || TO_CHAR(SQLCODE) || SQLERRM);
   utl_file.fremove('TEST_DIR','HELLO.TXT');
   NULL; 
   END; 

Plsql块调用第一个过程。

set serveroutput on;
begin
sharefle;
end;

此代码是您所询问的一个非常简单的示例。如果有任何异常,您可以检查过程2,在异常块中删除文件'HELLO.TXT'(过程1和过程2都有相同的文件)。我亲自检查了它,同样的工作。尝试创建自己的例外并自行检查。如有任何疑问,请发表评论。

注意:这绝对不是最好的方法。我已经告诉过你我们可以做到这一点。谢谢:)