回滚过程创建文件
Rollback procedure creating file
我有几个 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) 来创建文件
想法是..在所有过程都完成后创建文件,没有例外
UTL_FILE.FCLOSE
(或UTL_FILE.FFLUSH
)字面上写入磁盘。如果您不想写入磁盘,则不得写入磁盘 - 在所有数据都写入每个单独的缓冲区之前,不要关闭或刷新文件处理程序。
根据 n 的大小,您可能有很多打开的文件处理程序,其中缓冲了大量数据。这不会很漂亮。
最好创建另一个过程来调用 UTL_FILE.FREMOVE
,它会删除一个命名文件(假设有足够的权限)。
我会在 Oracle scheduler, given each procedure being a separate step in a chain you can define a rule using the scheduler chain condition syntax 中执行此操作以调用删除链中错误文件的过程。
除非您可以让您的文件系统参与两阶段提交(据我所知目前这是不可能的),否则协调文件输出与数据库事务将很困难,因为您的文件操作位于在您的数据库事务范围之外。
也就是说,总是存在一种理论上的情况,即某些事情在完全错误的时间发生并且您的数据库和文件系统不同步。 (有点让你感激 COMMIT
为我们所做的一切)。
无论如何,一种可能的策略是设计事物,以便 window 出现问题的时间尽可能短。例如,
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;
这里的想法是将所有文件写入临时文件。如果出现故障,您可以清理它们。没有进程可以看到 "real" 文件,因为你从未创建过它们。只有在最后,您才能通过重命名临时文件来使它们成为现实。
您的风险是您的前几个临时文件已成功重命名,但后续临时文件无法重命名并且 (A) 一个进程跳入并在您的异常处理程序可以删除它们之前看到它们或 (B)异常处理程序由于某种原因无法删除它们。
我喜欢这种方法,因为它将所有风险都与重命名文件联系在一起,这是一个非常安全的操作(因为它不需要额外的磁盘 space)。不太可能有的重命名成功,有的重命名失败。
这种方法可以有很多变化。但要记住的是,您并不是在这里实施坚如磐石的解决方案。总是有可能出现问题,因此根据您的容错能力,实施所需的任何检查(在您系统的其他地方)。
迈克尔,
您或许可以使用 --> utl_file.fremove(DIRECTORY_PATH,FILENAME);在异常中为了删除文件。
示例代码如下。
程序 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 具有相同的文件)。我亲自检查过它,同样有效。尝试创建自己的异常并自行检查。如有任何疑问,请发表评论。
注意:这绝不是最好的方法。我已经向您展示了我们可以这样做。谢谢 :)
我有几个 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) 来创建文件
想法是..在所有过程都完成后创建文件,没有例外
UTL_FILE.FCLOSE
(或UTL_FILE.FFLUSH
)字面上写入磁盘。如果您不想写入磁盘,则不得写入磁盘 - 在所有数据都写入每个单独的缓冲区之前,不要关闭或刷新文件处理程序。
根据 n 的大小,您可能有很多打开的文件处理程序,其中缓冲了大量数据。这不会很漂亮。
最好创建另一个过程来调用 UTL_FILE.FREMOVE
,它会删除一个命名文件(假设有足够的权限)。
我会在 Oracle scheduler, given each procedure being a separate step in a chain you can define a rule using the scheduler chain condition syntax 中执行此操作以调用删除链中错误文件的过程。
除非您可以让您的文件系统参与两阶段提交(据我所知目前这是不可能的),否则协调文件输出与数据库事务将很困难,因为您的文件操作位于在您的数据库事务范围之外。
也就是说,总是存在一种理论上的情况,即某些事情在完全错误的时间发生并且您的数据库和文件系统不同步。 (有点让你感激 COMMIT
为我们所做的一切)。
无论如何,一种可能的策略是设计事物,以便 window 出现问题的时间尽可能短。例如,
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;
这里的想法是将所有文件写入临时文件。如果出现故障,您可以清理它们。没有进程可以看到 "real" 文件,因为你从未创建过它们。只有在最后,您才能通过重命名临时文件来使它们成为现实。
您的风险是您的前几个临时文件已成功重命名,但后续临时文件无法重命名并且 (A) 一个进程跳入并在您的异常处理程序可以删除它们之前看到它们或 (B)异常处理程序由于某种原因无法删除它们。
我喜欢这种方法,因为它将所有风险都与重命名文件联系在一起,这是一个非常安全的操作(因为它不需要额外的磁盘 space)。不太可能有的重命名成功,有的重命名失败。
这种方法可以有很多变化。但要记住的是,您并不是在这里实施坚如磐石的解决方案。总是有可能出现问题,因此根据您的容错能力,实施所需的任何检查(在您系统的其他地方)。
迈克尔,
您或许可以使用 --> utl_file.fremove(DIRECTORY_PATH,FILENAME);在异常中为了删除文件。
示例代码如下。
程序 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 具有相同的文件)。我亲自检查过它,同样有效。尝试创建自己的异常并自行检查。如有任何疑问,请发表评论。
注意:这绝不是最好的方法。我已经向您展示了我们可以这样做。谢谢 :)