第七十五章 SQL命令 START TRANSACTION
开始一个事务。
大纲
START TRANSACTION [%COMMITMODE commitmode]
START TRANSACTION [transactionmodes]
参数
-
commitmode
- 可选-指定在当前进程中如何向数据库提交将来的事务。
取值包括EXPLICIT
、IMPLICIT
和NONE
。
默认是维护现有的提交模式;
进程的初始提交模式默认值是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
开始的事务必须通过发出显式COMMIT
或ROLLBACK
来结束。
START TRANSACTION
是可选的。
- 如果流程只查询数据(
SELECT
语句),可以使用SET TRANSACTION
来建立隔离级别。
不需要START TRANSACTION
。 - 如果进程正在修改数据,那么是否需要通过发出
START transaction
来显式地开始SQL
事务,这取决于进程的当前提交模式设置(也称为AutoCommit
设置)。
如果当前进程的提交模式是隐式的或显式的,则发出START TRANSACTION
是可选的。
如果忽略START TRANSACTION
,则在调用修改数据操作(DELETE
、UPDATE
或INSERT
)时,系统会自动启动一个事务。
如果指定START TRANSACTION
,则会立即启动一个事务,并且必须通过显式COMMIT
或ROLLBACK
来结束该事务。
当START TRANSACTION
启动一个事务时,它将$TLEVEL
事务级别计数器从0增加到1,表明事务正在进行。
还可以通过检查%INTRANSACTION
语句设置的SQLCODE
来确定事务是否在进行中。
当事务正在进行时发出START TRANSACTION
对$TLEVEL
或%INTRANSACTION
没有影响。
SQL不支持嵌套事务。
当事务已经在进行时发出START TRANSACTION
不会启动另一个事务,也不会返回错误代码。 SQL支持保存点,允许事务的部分回滚。
当发出SAVEPOINT
语句时,如果事务没有在进行中,则SAVEPOINT
将启动一个事务。
但是,不推荐使用这种方式启动事务。
如果事务操作未能成功完成,则会发出SQLCODE -400
。
设置参数
可以选择使用START TRANSACTION
来设置参数。
设置的参数立即生效。
但是,无论如何设置commitmode
参数,任何以START transaction
启动的事务都必须以显式COMMIT
或ROLLBACK
结束。
参数设置在当前进程期间继续有效,直到显式重置为止。
它们不会在事务结束时自动重置为默认值。
单个START TRANSACTION
语句可用于设置提交模式参数或事务模式参数,但不能同时设置两者。
要设置两者,可以发出set TRANSACTION
和START TRANSACTION
,或者两条START TRANSACTION
语句。
只有第一个START TRANSACTION
才会启动一个事务。
在发出START TRANSACTION
之后,可以在事务期间通过发出另一个START TRANSACTION
、SET TRANSACTION
或方法调用来更改这些参数设置。
更改commitmode
参数并不会删除使用显式COMMIT
或ROLLBACK
结束当前事务的需求。
可以使用SET TRANSACTION
语句来设置提交模式或事务模式参数,而不需要启动事务。
还可以使用方法调用在事务外部或事务内部设置这些参数。
%COMMITMODE
%COMMITMODE
关键字允许为当前流程指定自动事务启动和承诺行为。
START TRANSACTION %COMMITMODE
更改当前流程中所有未来事务的提交模式设置。
它不会影响由START transaction
语句发起的事务。
无论当前或设置提交模式,START TRANSACTION
都会立即启动一个事务,并且这个事务必须通过发出显式的commit
或ROLLBACK
来结束。
可用的%COMMITMODE
选项有:
-
IMPLICIT
隐式:启用自动事务承诺(初始流程默认值)。
当程序发出数据库修改操作(INSERT
、UPDATE
或DELETE
)时,SQL自动启动一个事务。
事务将继续进行,直到操作成功完成并SQL自动提交更改,或者操作无法在所有行上成功完成并SQL自动回滚整个操作。
每个数据库操作(INSERT
、UPDATE
或DELETE
)构成一个单独的事务。
成功完成数据库操作将自动清除回滚日志、释放锁并减少$TLEVEL
。
不需要COMMIT
语句。 -
EXPLICIT
:关闭自动事务承诺。
当程序发出第一个数据库修改操作(INSERT
、UPDATE
或DELETE
)时,SQL自动启动一个事务。
该交易将继续进行,直到明确达成协议。
成功完成后,发出COMMIT
语句。
如果数据库修改操作失败,则发出ROLLBACK
语句将数据库恢复到事务开始之前的位置。
在EXPLICIT
模式下,多个数据库修改操作可以组成一个事务。 -
NONE
:没有自动事务处理。
除非由START TRANSACTION
显式调用,否则不会初始化事务。
必须通过发出COMMIT
或ROLLBACK
语句显式地结束所有事务。
因此,事务中是否包含数据库操作以及事务中数据库操作的数量都是用户定义的。
TRUNCATE TABLE
不会在自动启动的事务中发生。
如果需要对TRUNCATE TABLE
进行日志记录和回滚,则必须显式指定START TRANSACTION
,并以显式COMMIT
或rollback
结束。
可以使用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 UNCOMMITTED
与READ - WRITE
访问不兼容;
试图在同一语句中同时指定这两个变量会导致SQLCODE -92
错误。 -
READ VERIFIED
声明来自其他事务的未提交数据立即可用,并且不执行锁操作。
这包括随后可能被回滚的更改。
然而,与READ UNCOMMITTED
不同的是,READ VERIFIED
事务将重新检查任何可能因未提交或新提交的数据而失效的条件,这将导致不满足查询条件的输出。
由于这种条件重新检查,READ VERIFIED
比READ UNCOMMITTED
更准确,但效率更低,应该只在可能发生对条件检查的数据的并发更新时使用。
READ VERIFIED
与READ - WRITE
访问不兼容;
试图在同一语句中同时指定这两个变量会导致SQLCODE -92
错误。 -
READ COMMITTED
表示只有那些已经提交的更改可以用于查询访问。
这确保了在数据库上以一致的状态执行查询,而不是在进行一组更改时执行,这组更改随后可能会回滚。
如果请求的数据已被更改,但更改尚未提交(或回滚),则查询将等待事务完成。
如果在等待该数据可用时发生锁定超时,则会发出SQLCODE -114
错误。
READ UNCOMMITTED还是READ VERIFIED?
下面的例子演示了READ UNCOMMITTED
和READ VERIFIED
之间的区别:
SELECT Name,SSN FROM Sample.Person WHERE Name >= 'M'
查询优化器可能首先选择从Name
索引中收集所有RowID
包含的符合>= 'M'
条件的Name
。
收集之后,每次访问一个RowID
,以检索Name
和SSN
字段用于输出。
并发运行的更新事务可以将一个RowID 72
的Person
的Name
字段从“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 TRANSACTION
、START TRANSACTION
或SetOption(“IsolationMode”)
方法调用显式重置。
由于COMMIT
或ROLLBACK
仅对数据更改有意义,而对数据查询没有意义,因此COMMIT
或ROLLBACK
操作对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 TSTART
和SQL 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`:
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
}