Evk*_*a'S 5 sql oracle plsql transactions
有人告诉我,以下代码不会帮助我检查重复性,因为在SELECT和UPDATE语句之前结果可能有所不同。
PROCEDURE AddNew(Pname VARCHAR2, Pcountry VARCHAR2)
AS
already_exists BOOLEAN;
BEGIN
SELECT COUNT(*)>0 INTO already_exists FROM Publishers WHERE name=Pname;
IF already_exists THEN
RAISE_APPLICATION_ERROR(-20014,'Publisher already exists!');
END IF;
INSERT INTO Publishers(id,name,country)
VALUES (NewPublisherId(),Pname,Pcountry);
END;
Run Code Online (Sandbox Code Playgroud)
这篇文章声称SELECT启动了一个事务: 为什么仅从数据库视图中进行选择时为什么会得到未结事务?
文档的这一部分另有建议:
事务隐式地开始于获得TX锁定的任何操作:
发出修改数据的语句时
发出SELECT ... FOR UPDATE语句时
使用SET TRANSACTION语句或DBMS_TRANSACTION包显式启动事务时
所以?SELECT是否启动事务?
后者是正确的:https://docs.oracle.com/cloud/latest/db112/SQLRF/statements_10005.htm#SQLRF01705
\n\n\n\n\n事务隐式地从任何获得 TX\n 锁的操作开始:
\n\n\n
\n- 当发出修改数据的语句时
\n- 当发出 SELECT ... FOR UPDATE 语句时
\n- 当使用 SET TRANSACTION 语句或 DBMS_TRANSACTION 包显式启动事务时
\n
但这真的不重要,从主要问题的角度来看——查看数据库中是否已经存在该记录。即使事务是明确启动使用的SET TRANSACTION ...,您的代码也根本不会检测到重复事务!
\n只需手动执行一个简单的测试,模拟两个同时会话中的过程,您就会看到:
CREATE TABLE Publishers(\n id int,\n name varchar2(100)\n);\nRun Code Online (Sandbox Code Playgroud)\n\n假设在会话 #1 中,过程于 8:00:00.0000 开始:
\n\nSQL> Set transaction name \'session 1\';\n\nTransaction set.\n\nSQL> select count(*) FROM Publishers where name = \'John\';\n\n COUNT(*)\n----------\n 0\n\nSQL> INSERT INTO Publishers(id,name) VALUES(1,\'John\');\n\n1 row created.\nRun Code Online (Sandbox Code Playgroud)\n\n假设在会话 #2 中,相同的过程在 8:00:00.0020 开始,就在会话 1 中进行插入之后,但仍在会话 #1 提交之前:
\n\nSQL> Set transaction name \'session 2\';\n\nTransaction set.\n\nSQL> select count(*) FROM Publishers where name = \'John\';\n\n COUNT(*)\n----------\n 0\nRun Code Online (Sandbox Code Playgroud)\n\n事务 #2 没有看到会话 1 所做的未提交的更改,因此会话 2 假设没有记录John,因此它也将其插入到表中:
SQL> INSERT INTO Publishers(id,name) VALUES(1,\'John\');\n\n1 row created.\nRun Code Online (Sandbox Code Playgroud)\n\n现在会话 1 提交:
\n\nSQL> Commit;\n\nCommit complete.\nRun Code Online (Sandbox Code Playgroud)\n\n几毫秒后,session2 也提交了:
\n\nSQL> Commit;\n\nCommit complete.\nRun Code Online (Sandbox Code Playgroud)\n\n最终结果是 - 即使事务已显式启动,也会出现重复的记录:
\n\nselect * from publishers;\n ID NAME \n---------- ----------------------------------------------------------------------------------------------------\n 1 John \n 1 John \nRun Code Online (Sandbox Code Playgroud)\n\n=========编辑=================
\n\n\n\n\n您可以通过在开头执行语句 SET TRANSACTION\n ISOLATION LEVEL SERIALIZABLE 来避免口是心非。\xe2\x80\x93 @Draex_
\n
许多人认为这ISOLATION LEVEL SERIALIZABLE会神奇地解决问题。不幸的是,这无济于事。
\n让我们通过一个简单的例子来看看它是如何工作的:
\n Session #1
SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;\n\nTransaction set.\n\nSQL> select count(*) FROM Publishers where name = \'John\';\n\n COUNT(*)\n----------\n 0\n\nSQL> INSERT INTO Publishers(id,name) VALUES(1,\'John\');\n\n1 row created.\nRun Code Online (Sandbox Code Playgroud)\n\n第 2 节
\n\nSQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;\n\nTransaction set.\n\nSQL> select count(*) FROM Publishers where name = \'John\';\n\n COUNT(*)\n----------\n 0\n\nSQL> INSERT INTO Publishers(id,name) VALUES(1,\'John\');\n\n1 row created.\nRun Code Online (Sandbox Code Playgroud)\n\n再次会话#1:
\n\nSQL> commit;\n\nCommit complete.\n\nSQL> select * from publishers;\n\n ID NAME\n---------- --------\n 1 John\nRun Code Online (Sandbox Code Playgroud)\n\n回到会话 #2
\n\nSQL> commit;\n\nCommit complete.\n\nSQL> select * from publishers;\n ID NAME\n ---------- --------\n 1 John\n 1 John\nRun Code Online (Sandbox Code Playgroud)\n\n正如你所看到的,它的魔力在于ISOLATION LEVEL SERIALIZABLE没有发挥作用。