sql - SELECT 是否在 PL/SQL 中启动事务

标签 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;

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

documentation的这部分否则建议:

A transaction implicitly begins with any operation that obtains a TX lock:

  • When a statement that modifies data is issued

  • When a SELECT ... FOR UPDATE statement is issued

  • When a transaction is explicitly started with a SET TRANSACTION statement or the DBMS_TRANSACTION package



所以? SELECT 是否启动事务?

最佳答案

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

A transaction implicitly begins with any operation that obtains a TX lock:

  • When a statement that modifies data is issued
  • When a SELECT ... FOR UPDATE statement is issued
  • When a transaction is explicitly started with a SET TRANSACTION statement or the DBMS_TRANSACTION package


不过真的没关系 ,从主要问题的角度来看——查看该记录是否已经存在于数据库中。即使交易是明确地开始使用 SET TRANSACTION ... ,您的代码根本没有检测到重复的交易!
只需在两个同时进行的 session 中手动模拟该过程进行一个简单的测试,您将看到:
CREATE TABLE Publishers(
    id int,
    name varchar2(100)
);

假设在 session #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.

假设在 session #2 中,相同的过程从 8:00:00.0020 开始,就在 session 1 中进行插入之后,但仍然在 session #1 提交之前:
SQL> Set transaction name 'session 2';

Transaction set.

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

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

事务 #2 看不到 session 1 所做的未提交更改,因此 session 2 假定没有记录 John ,所以它也将它插入到表中:
SQL>  INSERT INTO Publishers(id,name) VALUES(1,'John');

1 row created.

现在 session 1 提交:
SQL> Commit;

Commit complete.

几毫秒后 session2 也提交了:
SQL> Commit;

Commit complete.

最终的结果是 - 即使事务已明确启动,也会出现重复的记录:
select * from publishers;
        ID NAME                                                                                                
---------- ----------------------------------------------------------------------------------------------------
         1 John                                                                                                
         1 John         

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

You can avoid the duplicity by executing statement SET TRANSACTION ISOLATION LEVEL SERIALIZABLE in the beginning. – @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.

session #1 再次:
SQL> commit;

Commit complete.

SQL> select * from publishers;

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

并返回 session #2
SQL> commit;

Commit complete.

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

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

关于sql - SELECT 是否在 PL/SQL 中启动事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49455629/

相关文章:

sql - Redshift 除以零谜题

sql - 甲骨文 - 将日期分成几个季度

mysql - mysql select中的计数语句

php - 将值或变量传递给 Paypal 购物车

sql - Oracle:返回仅包含数字值的行的 SQL 查询

java - Oracle调用另一台机器上的Java方法

oracle - 如何在pl sql developer中运行sql函数

java - 如何使用 OracleTypeCHAR 元数据获取 Oracle 对象类型中声明的 VARCHAR2 属性的长度,而不考虑数据库字符集

database - Oracle sqlplus > 替换提示变量

sql - 按非字母顺序排序但按字母顺序显示在 postgresql 中的分组依据/排序依据