【问题标题】:Does SELECT start transaction in PL/SQLSELECT 是否在 PL/SQL 中启动事务
【发布时间】:2018-03-23 17:47:33
【问题描述】:

有人告诉我,以下代码无法帮助我检查重复性,因为在 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;

这篇文章声称 SELECT 启动了一个事务: Why do I get an open transaction when just selecting from a database View?

这部分documentation 另有建议:

交易隐含地从任何获得 TX 的操作开始 锁:

  • 发出修改数据的语句时

  • 当发出 SELECT ... FOR UPDATE 语句时

  • 使用 SET TRANSACTION 语句或 DBMS_TRANSACTION 包显式启动事务时

所以? SELECT 是否启动事务?

【问题讨论】:

  • 过程的参数不能由 VARCHAR2 定义,其长度与使用 VARCHAR2(40) 或 (30) 的语句中的长度相同。 SELECT COUNT(*)>0 INTO 不允许...
  • 如果您阅读了您链接的完整帖子,而不仅仅是接受的答案,您会看到SELECT 语句不会启动事务。
  • 顺便问一下,您是在询问SELECT 是否启动了事务,还是在寻求有关存储过程的帮助?我不清楚。
  • 无论如何,我肯定看到表格在SELECTINSERT 之间得到更新,无论文档可能会或可能不会说什么。以这种方式编写存储过程绝对不是一个好习惯。
  • 这就是 Nature 给我们 UNIQUE 约束的原因,以便我们可以在多用户环境中强制执行不重复值。

标签: sql oracle plsql transactions


【解决方案1】:

后者是真的:https://docs.oracle.com/cloud/latest/db112/SQLRF/statements_10005.htm#SQLRF01705

交易隐含地从任何获得 TX 的操作开始 锁:

  • 发出修改数据的语句时
  • 当发出 SELECT ... FOR UPDATE 语句时
  • 使用 SET TRANSACTION 语句或 DBMS_TRANSACTION 包显式启动事务时

不过真的没关系,从主要问题的角度来看——看看数据库中是否已经存在记录。即使事务是使用SET TRANSACTION ... 明确启动的,您的代码也根本不会检测到重复事务!

只需手动进行一个简单的测试,在两个同时进行的会话中模拟该过程,您就会看到:

CREATE TABLE Publishers(
    id int,
    name varchar2(100)
);

假设在会话 #1 中,程序从 8:00:00.0000 开始:

SQL> Set transaction name 'session 1';

Transaction set.

SQL> select count(*) FROM Publishers where name = 'John';

  COUNT(*)
----------
         0

SQL> INSERT INTO Publishers(id,name) VALUES(1,'John');

1 row created.

假设在会话 #2 中,相同的过程从 8:00:00.0020 开始,就在会话 1 中进行插入之后,但仍然在会话 #1 提交之前:

SQL> Set transaction name 'session 2';

Transaction set.

SQL> select count(*) FROM Publishers where name = 'John';

  COUNT(*)
----------
         0

事务 #2 没有看到会话 1 完成的未提交更改,因此会话 2 假设没有记录 John,因此它也将其插入到表中:

SQL>  INSERT INTO Publishers(id,name) VALUES(1,'John');

1 row created.

现在会话 1 提交:

SQL> Commit;

Commit complete.

几毫秒后 session2 也提交了:

SQL> Commit;

Commit complete.

最终的结果是 - 即使事务已明确启动,但仍有重复记录:

select * from publishers;
        ID NAME                                                                                                
---------- ----------------------------------------------------------------------------------------------------
         1 John                                                                                                
         1 John         

========== 编辑 =================

您可以通过执行语句 SET TRANSACTION 来避免重复 ISOLATION LEVEL SERIALIZABLE 开始。 – @Draex_

很多人认为ISOLATION LEVEL SERIALIZABLE 会神奇地解决问题。 很遗憾,这无济于事

让我们通过一个简单的例子来看看它是如何工作的:

Session #1

SQL>  SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

Transaction set.

SQL> select count(*) FROM Publishers where name = 'John';

  COUNT(*)
----------
         0

SQL> INSERT INTO Publishers(id,name) VALUES(1,'John');

1 row created.

第 2 场会议

SQL> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

Transaction set.

SQL>  select count(*) FROM Publishers where name = 'John';

  COUNT(*)
----------
         0

SQL> INSERT INTO Publishers(id,name) VALUES(1,'John');

1 row created.

第 1 次会议再次:

SQL> commit;

Commit complete.

SQL> select * from publishers;

        ID  NAME
----------  --------
         1  John

然后回到会话 #2

SQL> commit;

Commit complete.

SQL> select * from publishers;
            ID  NAME
    ----------  --------
             1  John
             1  John

如您所见,ISOLATION LEVEL SERIALIZABLE 的魔力不起作用。

【讨论】:

  • 你可以通过在开头执行语句SET TRANSACTION ISOLATION LEVEL SERIALIZABLE来避免重复。
  • @Draex_ 我已经用一个示例更新了我的问题,这如何与ISOLATION LEVEL SERIALIZABLE 一起工作,请看一下。
  • 啊哈,谢谢。你知道为什么会这样吗?我认为这是 Phantom read 的问题,就像在On Transaction Isolation Levels 中一样,这在 SERIALIZABLE 模式下不应该发生。
  • 谢谢。你知道我怎样才能实现我想要做的事情吗?
  • @Draex_ Do you know why is that so? - 因为会话看不到来自其他会话的未提交更改。隔离级别 Serializable 不会以任何方式神奇地改变这种行为。它只序列化锁
猜你喜欢
  • 1970-01-01
  • 2012-03-09
  • 2012-02-08
  • 1970-01-01
  • 2014-11-13
  • 2018-03-30
  • 2010-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多