我有几个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,没有异常
答案 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(目录路径,文件名);在异常中删除文件。
示例代码如下。
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;
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;
set serveroutput on;
begin
sharefle;
end;
此代码是您所询问的一个非常简单的示例。如果有任何异常,您可以检查过程2,在异常块中删除文件'HELLO.TXT'(过程1和过程2都有相同的文件)。我亲自检查了它,同样的工作。尝试创建自己的例外并自行检查。如有任何疑问,请发表评论。
注意:这绝对不是最好的方法。我已经告诉过你我们可以做到这一点。谢谢:)