文章
姚 鑫 · 十一月 13, 2021 阅读大约需 11 分钟

第七十五章 SQL命令 START TRANSACTION

第七十五章 SQL命令 START TRANSACTION

开始一个事务。

大纲

START TRANSACTION [%COMMITMODE commitmode]

START TRANSACTION [transactionmodes]

参数

  • commitmode - 可选-指定在当前进程中如何向数据库提交将来的事务。
    取值包括EXPLICITIMPLICITNONE
    默认是维护现有的提交模式;
    进程的初始提交模式默认值是IMPLICIT
  • transactionmodes - 可选—指定事务的隔离模式和访问模式。
    可以将隔离模式、访问模式或这两种模式的值指定为逗号分隔的列表。隔离模式的有效值为ISOLATION LEVEL READ COMMITTED, ISOLATION LEVEL READ UNCOMMITTED, ISOLATION LEVEL READ VERIFIED. 默认 ISOLATION LEVEL READ UNCOMMITTED.访问模式的有效值为“READ ONLY”“READ WRITE”
    注意,只有隔离级别READ COMMITTED与读写模式READ WRITE兼容。

描述

START TRANSACTION语句启动一个事务。
START TRANSACTION立即启动一个事务,而不管当前的提交模式设置如何。
无论当前的提交模式设置如何,以START transaction开始的事务必须通过发出显式COMMITROLLBACK来结束。

START TRANSACTION是可选的。

  • 如果流程只查询数据(SELECT语句),可以使用SET TRANSACTION来建立隔离级别。
    不需要START TRANSACTION
  • 如果进程正在修改数据,那么是否需要通过发出START transaction来显式地开始SQL事务,这取决于进程的当前提交模式设置(也称为AutoCommit设置)。
    如果当前进程的提交模式是隐式的或显式的,则发出START TRANSACTION是可选的。
    如果忽略START TRANSACTION,则在调用修改数据操作(DELETEUPDATEINSERT)时,系统会自动启动一个事务。
    如果指定START TRANSACTION,则会立即启动一个事务,并且必须通过显式COMMITROLLBACK来结束该事务。

START TRANSACTION启动一个事务时,它将$TLEVEL事务级别计数器从0增加到1,表明事务正在进行。
还可以通过检查%INTRANSACTION语句设置的SQLCODE来确定事务是否在进行中。
当事务正在进行时发出START TRANSACTION$TLEVEL%INTRANSACTION没有影响。

SQL不支持嵌套事务。
当事务已经在进行时发出START TRANSACTION不会启动另一个事务,也不会返回错误代码。 SQL支持保存点,允许事务的部分回滚。

当发出SAVEPOINT语句时,如果事务没有在进行中,则SAVEPOINT将启动一个事务。
但是,不推荐使用这种方式启动事务。

如果事务操作未能成功完成,则会发出SQLCODE -400

设置参数

可以选择使用START TRANSACTION来设置参数。
设置的参数立即生效。
但是,无论如何设置commitmode参数,任何以START transaction启动的事务都必须以显式COMMITROLLBACK结束。
参数设置在当前进程期间继续有效,直到显式重置为止。
它们不会在事务结束时自动重置为默认值。

单个START TRANSACTION语句可用于设置提交模式参数或事务模式参数,但不能同时设置两者。
要设置两者,可以发出set TRANSACTIONSTART TRANSACTION,或者两条START TRANSACTION语句。
只有第一个START TRANSACTION才会启动一个事务。

在发出START TRANSACTION之后,可以在事务期间通过发出另一个START TRANSACTIONSET TRANSACTION或方法调用来更改这些参数设置。
更改commitmode参数并不会删除使用显式COMMITROLLBACK结束当前事务的需求。

可以使用SET TRANSACTION语句来设置提交模式或事务模式参数,而不需要启动事务。
还可以使用方法调用在事务外部或事务内部设置这些参数。

%COMMITMODE

%COMMITMODE关键字允许为当前流程指定自动事务启动和承诺行为。
START TRANSACTION %COMMITMODE更改当前流程中所有未来事务的提交模式设置。
它不会影响由START transaction语句发起的事务。
无论当前或设置提交模式,START TRANSACTION都会立即启动一个事务,并且这个事务必须通过发出显式的commitROLLBACK来结束。

可用的%COMMITMODE选项有:

  • IMPLICIT隐式:启用自动事务承诺(初始流程默认值)。
    当程序发出数据库修改操作(INSERTUPDATEDELETE)时,SQL自动启动一个事务。
    事务将继续进行,直到操作成功完成并SQL自动提交更改,或者操作无法在所有行上成功完成并SQL自动回滚整个操作。
    每个数据库操作(INSERTUPDATEDELETE)构成一个单独的事务。
    成功完成数据库操作将自动清除回滚日志、释放锁并减少$TLEVEL
    不需要COMMIT语句。
  • EXPLICIT:关闭自动事务承诺。
    当程序发出第一个数据库修改操作(INSERTUPDATEDELETE)时,SQL自动启动一个事务。
    该交易将继续进行,直到明确达成协议。
    成功完成后,发出COMMIT语句。
    如果数据库修改操作失败,则发出ROLLBACK语句将数据库恢复到事务开始之前的位置。
    EXPLICIT模式下,多个数据库修改操作可以组成一个事务。
  • NONE:没有自动事务处理。
    除非由START TRANSACTION显式调用,否则不会初始化事务。
    必须通过发出COMMITROLLBACK语句显式地结束所有事务。
    因此,事务中是否包含数据库操作以及事务中数据库操作的数量都是用户定义的。

TRUNCATE TABLE不会在自动启动的事务中发生。
如果需要对TRUNCATE TABLE进行日志记录和回滚,则必须显式指定START TRANSACTION,并以显式COMMITrollback结束。

可以使用SetOption()方法在ObjectScript中设置%COMMITMODE,如下set status=$SYSTEM.SQL.Util.SetOption("AutoCommit",intval,.oldval)
可用的方法值为0 (NONE)、1 (IMPLICIT)和2 (EXPLICIT)。

注意:分片表总是处于No AutoCommit模式(SetOption("AutoCommit",0)),这意味着所有对分片表的插入、更新和删除都是在事务范围之外执行的。

隔离级别

可以为发出查询的进程指定“隔离级别”。
“隔离级别”选项允指定正在进行的更改是否可用于查询的读访问。
如果另一个并发进程正在执行对表的插入或更新,并且对表的更改在事务中,那么这些更改正在进行中,并且可能会回滚。
通过为正在查询该表的流程设置ISOLATION LEVEL,可以指定是否希望在查询结果中包含或排除这些正在进行的更改。

  • READ UNCOMMITTED表示所有更改都可以立即用于查询访问。
    这包括随后可能被回滚的更改。
    READ UNCOMMITTED确保查询将在不等待并发插入或更新进程的情况下返回结果,并且不会因为锁定超时错误而失败。
    然而,READ UNCOMMITTED的结果可能包括未提交的值;
    这些值在内部可能不一致,因为插入或更新操作只部分完成,这些值可能随后被回滚。
    如果查询进程不在显式事务中,或者事务没有指定隔离级别,则READ UNCOMMITTED是默认值。
    READ UNCOMMITTEDREAD - WRITE访问不兼容;
    试图在同一语句中同时指定这两个变量会导致SQLCODE -92错误。
  • READ VERIFIED声明来自其他事务的未提交数据立即可用,并且不执行锁操作。
    这包括随后可能被回滚的更改。
    然而,与READ UNCOMMITTED不同的是,READ VERIFIED事务将重新检查任何可能因未提交或新提交的数据而失效的条件,这将导致不满足查询条件的输出。
    由于这种条件重新检查,READ VERIFIEDREAD UNCOMMITTED更准确,但效率更低,应该只在可能发生对条件检查的数据的并发更新时使用。
    READ VERIFIEDREAD - WRITE访问不兼容;
    试图在同一语句中同时指定这两个变量会导致SQLCODE -92错误。
  • READ COMMITTED表示只有那些已经提交的更改可以用于查询访问。
    这确保了在数据库上以一致的状态执行查询,而不是在进行一组更改时执行,这组更改随后可能会回滚。
    如果请求的数据已被更改,但更改尚未提交(或回滚),则查询将等待事务完成。
    如果在等待该数据可用时发生锁定超时,则会发出SQLCODE -114错误。

READ UNCOMMITTED还是READ VERIFIED?

下面的例子演示了READ UNCOMMITTEDREAD VERIFIED之间的区别:

SELECT Name,SSN FROM Sample.Person WHERE Name >= 'M' 

查询优化器可能首先选择从Name索引中收集所有RowID包含的符合>= 'M'条件的Name
收集之后,每次访问一个RowID,以检索NameSSN字段用于输出。
并发运行的更新事务可以将一个RowID 72PersonName字段从“Smith”更改为“Abel”,该字段位于查询的rowwid集合和它对表的逐行访问之间。
在本例中,索引中的RowID集合将包含不再符合Name >= 'M'条件的行的RowID

READ UNCOMMITTED查询处理假设Name >= 'M'条件已经被索引满足,并且将输出从索引中收集的每个RowID在表中出现的任何Name
因此,在本例中,它将输出一个名称为'Abel'的行,该行不满足条件。

READ VERIFIED查询处理注意到,它正在从表中为output (Name)检索一个字段,该字段参与了之前应该由索引满足的条件,然后重新检查条件,以防在检查索引之后字段值发生变化。
在重新检查时,它注意到该行不再满足条件,并将其从输出中删除。
只有输出所需的值才会重新检查其条件:在本例中,SELECT SSN FROM Person WHERE Name >= 'M'将输出RowID为72的行。

READ COMMITTED异常

ISOLATION LEVEL read committed生效时,可以通过设置ISOLATION LEVEL read committed或SetOption()方法,如下SET status=$SYSTEM.SQL.Util.SetOption("IsolationMode",1,.oldval)
SQL只能检索已提交数据的更改。
然而,也有一些明显的例外:

  • 查询永远不会返回已删除的行,即使删除该行的事务正在进行,且删除可能随后回滚。
    ISOLATION LEVEL READ COMMITTED确保插入和更新处于一致状态,而不是删除。
  • 如果查询包含聚合函数,则聚合结果将返回数据的当前状态,而与指定的隔离级别无关。
    因此,聚合结果中包含正在进行的插入和更新(随后可能回滚)。
    正在进行的删除(随后可能会回滚)不包括在聚合结果中。
    这是因为聚合操作需要访问表中的许多行数据。
  • 包含DISTINCT子句或GROUP BY子句的SELECT查询不受隔离级别设置的影响。
    包含这些子句之一的查询将返回数据的当前状态,包括可能随后回滚的正在进行的更改。
    这是因为这些查询操作需要访问表中的许多行数据。
  • 带有%NOLOCK关键字的查询。

注意:在使用ECP(企业缓存协议)的 IRIS实现上,与READ UNCOMMITTED相比,使用READ COMMITTED可能会导致明显的性能下降。
在定义包含ECP的事务时,开发人员应该权衡READ UNCOMMITTED的优越性能和READ COMMITTED的更高数据准确性。

有效隔离级别

可以使用set TRANSACTION(不启动事务)、START TRANSACTION(设置隔离模式并启动事务)或SetOption(“IsolationMode”)方法调用为进程设置隔离级别。

指定的隔离级别保持有效,直到由SET TRANSACTIONSTART TRANSACTIONSetOption(“IsolationMode”)方法调用显式重置。
由于COMMITROLLBACK仅对数据更改有意义,而对数据查询没有意义,因此COMMITROLLBACK操作对ISOLATION LEVEL设置没有影响。

在查询开始时有效的“隔离级别”在查询期间仍然有效。

可以使用GetOption(“IsolationMode”)方法调用确定当前进程的隔离级别。
还可以使用SetOption(“IsolationMode”)方法调用为当前进程设置隔离模式。
这些方法将READ UNCOMMITTED(默认值)指定为0,READ COMMITTED指定为1,READ VERIFIED指定为3
指定任何其他数值将保持隔离模式不变。
如果将隔离模式设置为当前隔离模式,则不会发生错误或更改。
以下示例显示了这些方法的使用:

ClassMethod SetTransaction1()
{
    w $SYSTEM.SQL.GetOption("IsolationMode")," 默认",!
    &sql(
        START TRANSACTION ISOLATION LEVEL READ COMMITTED,READ WRITE
    )
    w $SYSTEM.SQL.GetOption("IsolationMode")," TART TRANSACTION之后",!
    d $SYSTEM.SQL.SetOption("IsolationMode",0,.stat)
    if stat=1 {
        w $SYSTEM.SQL.GetOption("IsolationMode")," after IsolationMode=0 call",! 
    } else { WRITE "Set IsolationMode error" 
    }
    &sql(COMMIT)
}

隔离模式和访问模式必须始终兼容。
更改访问模式将更改隔离模式,示例如下:

ClassMethod SetTransaction2()
{
    w $SYSTEM.SQL.GetOption("IsolationMode")," default",!
    &sql(
        SET TRANSACTION ISOLATION LEVEL READ COMMITTED,READ WRITE
    )
    w $SYSTEM.SQL.GetOption("IsolationMode")," after SET TRANSACTION",!
    &sql(START TRANSACTION READ ONLY)
    w $SYSTEM.SQL.GetOption("IsolationMode")," after changing access mode",!
    &sql(COMMIT)
}

ObjectScript和SQL事务

ObjectScript和SQL事务命令是完全兼容和可互换的,但有以下例外:

如果当前没有事务,ObjectScript TSTARTSQL START TRANSACTION都会启动一个事务。
但是,START TRANSACTION不支持嵌套事务。
因此,如果需要(或可能需要)嵌套事务,最好使用TSTART启动事务。
如果需要与SQL标准兼容,请使用START TRANSACTION

ObjectScript事务处理为嵌套事务提供了有限的支持。
SQL事务处理为事务中的保存点提供支持。

如果事务涉及SQL数据修改语句,则应该使用SQL START transaction语句启动事务,并使用SQL COMMIT语句提交事务。
(这些语句可以是显式的,也可以是隐式的,具体取决于%COMMITMODE设置。)
使用TSTART/TCOMMIT嵌套的方法可以包含在事务中,只要它们不初始化事务。
方法和存储过程通常不应该使用SQL事务控制语句,除非按照设计,它们是事务的主控制器。
存储过程通常不应该使用SQL事务控制语句,因为这些存储过程通常是从ODBC/JDBC调用的,ODBC/JDBC有自己的事务控制模型。

示例

下面的嵌入式SQL示例使用两个START TRANSACTION语句来启动事务并设置其参数。
注意,第一个START TRANSACTION启动一个事务,设置提交模式并增加$TLEVEL事务级别计数器。
第二个START TRANSACTION为当前事务中的查询读操作设置隔离模式,但不增加$TLEVEL,因为事务已经启动。
SAVEPOINT语句增加$TLEVEL:

ClassMethod StartTransaction()
{
    &sql(SET TRANSACTION %COMMITMODE EXPLICIT)
    w !,"设置事务提交模式, SQLCODE=",SQLCODE
    w !,"事务级别=",$TLEVEL
    &sql(SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED)
    w !,"设置事务隔离模式, SQLCODE=",SQLCODE
    w !,"事务级别=",$TLEVEL
    &sql(START TRANSACTION)
    w !,"开始事务, SQLCODE=",SQLCODE
    w !,"事务级别==",$TLEVEL
    &sql(SAVEPOINT a)
    w !,"设置保存点, SQLCODE=",SQLCODE
    w !,"事务级别==",$TLEVEL
    &sql(COMMIT)
    w !,"提交事务, SQLCODE=",SQLCODE
    w !,"事务级别==",$TLEVEL
}
0
0 26
讨论 (0)1
登录或注册以继续