在SQL或PLSQL中是否有办法确保没有提交任何内容?因为,有时调用函数/过程并且后果未知。例如,它可以触发隐式提交。有没有办法防止这种情况?
答案 0 :(得分:2)
我在Oracle数据库中提供示例
这是针对SQL语句DML。
SQL> set transaction read only;
Transaction set.
SQL> update t set t=14;
update t set t=14
*
ERROR at line 1:
ORA-01456: may not perform insert/delete/update operation inside a READ ONLY
transaction
这用于调用已提交的程序。
SQL>alter session DISABLE COMMIT IN PROCEDURE ;
SQL>exec procedureHavingCommit(10);
BEGIN procedureHavingCommit(10); END;
*
ERROR at line 1:
ORA-00034: cannot COMMIT in current PL/SQL session
ORA-06512: at "ND210.DRMOP_UTIL", line 332
ORA-06512: at "ND210.DRMOP_UTIL", line 1664
ORA-00034: cannot COMMIT in current PL/SQL session
ORA-06512: at line 1
答案 1 :(得分:1)
如果您想确保不会修改任何内容,那么"设置事务只读#34;是正确的答案。
否则:如果您正在编写一些调用其他程序员编写的其他程序的代码,并且您主要担心的是这些其他程序可能会发出不需要的提交(或者将来可能会被修改以发出不需要的提交),所以您要抓住在它们造成损害之前,我有一个你可以找到有用的解决方案,我目前在我的代码中用于这个目的:
让我们说你的程序这样做:
procedure MyProcedureThatModifiesData is
begin
update mytables....
SomeOthersProcedure;
update myothertables ...;
commit; -- having a commit in a stored procedure
-- is a bad idea: i wrote this only to mimick the global
-- application behaviour
end;
并且您希望确保,如果SomeOtherProcedure的作者修改了在其中插入提交的过程,则此提交将被阻止并回滚。
使用我的解决方案,代码变为:
procedure MyProcedureThatModifiesData is
begin
pkg_block_unwanted_commits.DisableCommits; // <<!!
update mytables....
SomeOthersProcedure;
update myothertables ...;
pkg_block_unwanted_commits.ReenableCommits; // <<!!
commit; -- having a commit in a stored procedure
-- is a bad idea: i wrote this only to mimick the global
-- application behaviour
end;
让我解释使其成为可能的想法:您只需要一个包含延迟约束的表。 只有当您发出&#34; commit&#34;时才会检查延迟约束:如果数据未提交则可能违反约束,但如果您尝试提交,则会回滚整个事务并发生oracle错误提高。
现在重点是:如果您在程序开始时故意插入一些违反延迟约束的数据,您将获得您要求的确切内容:回滚而不是提交。 要重新启用提交,您所要做的就是删除违反约束的数据。
一个基本的实现可能是这样的:
procedure MyProcedureThatModifiesData is
begin
-- this update "disables" the commits
update myspecialtable set
myfield=unacceptable_value_that_violates_the_constraint;
update mytables....
SomeOthersProcedure;
update myothertables ...;
-- this update "re-enables" the commits
update myspecialtable set
myfield=valid_value;
commit;
end;
上述基本实现的另一个步骤是使&#34; myspecialtable&#34;一个全局临时表(种类:在提交时保留行),因此它只包含在oracle会话期间写入的临时值,并且不会永久存储在数据库中。此外,通过这种方式,其他会话将能够在此特殊表中编写自己的数据而不会干扰您的表。
完整的解决方案就是这个:
create or replace package pkg_block_unwanted_commits is
-- we will implement these two in order to allow nested calls:
-- each DisableCommit must be paired with her EnableCommits.
-- commits will be actually enabled only when each DisableCommit (including
-- nested calls) has been closed by her pairing EnableCommit
procedure DisableCommits;
procedure ReenableCommits;
end;
/
-- this is the temporary table we will use for the above
create global temporary table tbl_block_unwanted_commits
(
-- this primary key, along with the "chk_only_one_row" constraint ensures that
-- this table can contain only one row
only_one_row char(1) default 'X' primary key,
-- it keeps track of the number of "opened" DisableCommits calls:
-- we can commit only if nest_counter is zero
nest_counter number not null
)
on commit preserve rows
/
-- this one, considering that "only_one_row" is the primary key
-- ensures we will have only one row in the table (just to be safe)
alter table tbl_block_unwanted_commits add constraint
chk_only_one_row check (only_one_row='X')
/
-- this is just to reveal errors in our program if we call EnableCommits without having called DisableCommits
-- (mispaired calls)
alter table tbl_block_unwanted_commits add constraint
chk_unbalanced_enables check (nest_counter >=0)
/
-- this one is the constraint that actually does the trick of blocking commits
-- we can commit only if whe have active calls to "disablecommits" that have not been paired with corresponding "ReenableCommits"
alter table tbl_block_unwanted_commits add constraint
chk_blocked_commits check (nest_counter = 0) deferrable initially deferred
/
create or replace package body pkg_block_unwanted_commits is
procedure DisableCommits is
begin
update tbl_block_unwanted_commits set nest_counter = nest_counter +1;
if sql%notfound then
insert into tbl_block_unwanted_commits(only_one_row,nest_counter)
values ('X',1);
end if;
end;
procedure ReenableCommits is
begin
update tbl_block_unwanted_commits set nest_counter = nest_counter -1;
end;
end;
/
希望这会有所帮助
答案 2 :(得分:0)
我将在上面的程序中对ALTER SESSION DISABLE COMMIT进行投票。但是,正如已经指出的那样 - 如果你已经在交易中,这种方法并不奏效。也就是说,您处于打算提交的情况,但在准备好之前,您不希望调用过程提交。
在这种情况下我做的是写一些东西来检测COMMIT是否已经发生。它不会阻止提交,但它会导致我的程序出错 - 即使提交没有任何伤害。
这样,我100%肯定会在测试期间检测到COMMIT正在发生并且可以在设计中处理它。
以下是我所描述的方法说明的链接:https://stackoverflow.com/a/31752576/5174436