事务不回滚的例子,请高手指教

javaflyers 2004-08-24 10:35:58
在自动提交事务模式下,在查询分析器中输入如下 SQL语句:

CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3))
/*在这个事务内部,有个运行时错误,就是往主键中插入相同的值,但是事务不会回滚*/
begin tran
INSERT INTO TestBatch VALUES (1, 'aaa')
INSERT INTO TestBatch VALUES (2, 'bbb')
INSERT INTO TestBatch VALUES (1, 'ccc') /* 主键相同错误 */
commit
SELECT * FROM TestBatch
/* Returns rows 1 and 2 */

运行以后出现如下信息:

(所影响的行数为 1 行)


(所影响的行数为 1 行)

服务器: 消息 2627,级别 14,状态 1,行 1
违反了 PRIMARY KEY 约束 'PK__TestBatch__22AA2996'。不能在对象 'TestBatch' 中插入重复键。
语句已终止。

(所影响的行数为 2 行)


这种错误 ,事务为什么不回滚??
...全文
293 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
zjcxc 2004-08-25
  • 打赏
  • 举报
回复
主要是搞不清楚还有多少个这种特例
pbsql 2004-08-25
  • 打赏
  • 举报
回复
楼上的是特例,一般程序中不会发生那样的错误(不会那样去写)

普通的违反约束等完全可以用 set xact_abort on 来实现出错时自动回滚
zjcxc 2004-08-25
  • 打赏
  • 举报
回复
--sql中的错误处理太弱了,不知道你看了下面的测试后,对sql的错误处理还有多大的信心


--演示:无法用 set xact_abort on 来自动回滚事务
set xact_abort on --我们希望能自动回滚事务
begin tran
create table #t(id int)
insert #t select 1
select * from newid()
commit tran
go

select * from #t
rollback tran
/*--测试结果


(所影响的行数为 1 行)

服务器: 消息 208,级别 16,状态 1,行 5
对象名 'newid' 无效。

id
-----------
1

(所影响的行数为 1 行)
--*/

/*--结论3:

我们希望 set xact_abort on 可以实现出错时自动回滚事务
但结果令我们希望,出错时,事务并没有被回滚
因为我们查询到了#t的结果,而且最后的回滚语句也并没有报错
--*/
zjcxc 2004-08-25
  • 打赏
  • 举报
回复
可以那样理解.
javaflyers 2004-08-25
  • 打赏
  • 举报
回复
都 晕了,我总结一下, 请高手指正!!
自动提交事务
每条单独的语句都是一个事务。
------------------------------
因此以下三个语句,实际上是三个事务。
begin tran
INSERT INTO TestBatch VALUES (1, 'aaa')
INSERT INTO TestBatch VALUES (2, 'bbb')
INSERT INTO TestBatch VALUES (1, 'ccc') /* 主键相同错误 */
commit
相当 于:
begin tran
begin tran inner1
INSERT INTO TestBatch VALUES (1, 'aaa')
commit tran inner1
begin tran inner2
INSERT INTO TestBatch VALUES (2, 'bbb')
commit tran inner2
begin tran inner3
INSERT INTO TestBatch VALUES (1, 'ccc') /* 主键相同错误 */
commit tran inner3
commit

内部三个应该是并列,而不是嵌套的。是这样吗??请高手指正。

另外一点很重要的是风云兄所说的: SET XACT_ABORT ON !!!
rouqu 2004-08-25
  • 打赏
  • 举报
回复
When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back. When OFF, only the Transact-SQL statement that raised the error is rolled back and the transaction continues processing. Compile errors, such as syntax errors, are not affected by SET XACT_ABORT.

学习再支持风云:)
netcoder 2004-08-25
  • 打赏
  • 举报
回复
楼主的问题可以用联机帮助的这句来解释
-------------------------------
自动提交事务
每条单独的语句都是一个事务。
------------------------------
因此以下三个语句,实际上是三个事务,所以当最后一句出错,只回滚最后一句
begin tran
INSERT INTO TestBatch VALUES (1, 'aaa')
INSERT INTO TestBatch VALUES (2, 'bbb')
INSERT INTO TestBatch VALUES (1, 'ccc') /* 主键相同错误 */
commit
另外判断上一句执行是否出错 通常用if @@error<>0 来判断,没有好的办法
pbsql 2004-08-25
  • 打赏
  • 举报
回复
楼上,不管事务哪种类型他有一部分出现错误整个都要回滚,那可不一定,你去看看:
SET XACT_ABORT ON
SET XACT_ABORT OFF
rouqu 2004-08-25
  • 打赏
  • 举报
回复
to:javaflyers(javaflyers) ( ) 信誉:100
不管事务哪种类型他有一部分出现错误整个都要回滚 事务天生就是这么定义的
我上面的那里不是标注了一下吗?
因为你是自动提交模式,一个语句就是一个事务 所以前面2句即2个事务 成功之后返回受影响的行数 第三个事务提交失败才会报错并回卷 第四个也是正常提交执行 明白了吗?
pbsql 2004-08-25
  • 打赏
  • 举报
回复
所以应改成下面这样:
CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3))
/*在这个事务内部,有个运行时错误,就是往主键中插入相同的值,但是事务不会回滚*/
SET XACT_ABORT ON
begin tran
INSERT INTO TestBatch VALUES (1, 'aaa')
INSERT INTO TestBatch VALUES (2, 'bbb')
INSERT INTO TestBatch VALUES (1, 'ccc') /* 主键相同错误 */
commit
SET XACT_ABORT OFF
SELECT * FROM TestBatch
/* Returns rows 1 and 2 */
pbsql 2004-08-25
  • 打赏
  • 举报
回复
如果批处理中出现运行时语句错误(如违反约束),那么 SQL Server 中默认的行为将是只回滚产生该错误的语句。可以使用 SET XACT_ABORT 语句改变该行为。在 SET XACT_ABORT ON 语句执行之后,任何运行时语句错误都将导致当前事务自动回滚。编译错误(如语法错误)不受 SET XACT_ABORT 的影响。
jancyer 2004-08-25
  • 打赏
  • 举报
回复
1:首先 hdhai9451(※★AC米兰☆※) 的做法去掉了if后在我的机子上是对的。可以提交,也可以回滚。

2:
事务
事务是单个的工作单元。如果某一事务成功,则在该事务中进行的所有数据更改均会提交,成为数据库中的永久组成部分。如果事务遇到错误且必须取消或回滚,则所有数据更改均被清除。

/*
事务是一个工作单元,要么全做,要么全不做,
如:
USE pubs
CREATE TABLE test (c1 int)
GO
BEGIN TRANSACTION
GO
INSERT INTO test VALUES (1)
GO
INSERT INTO test VALUES ('aaa')
GO
COMMIT TRANSACTION
GO
SELECT *
FROM test
当尝试数据类型转换失败时,SQL Server 返回一个错误,事务回滚!
但是也有例外:
在事务复制过程中,可以指定在分发进程中可以跳过的错误。通常,当分发代理程序以连续模式运行,并且在遇到错误时,该代理程序和分发进程都会停止。通过指定不希望干扰复制的预期的错误或错误,该代理程序将记录这些错误信息,然后继续运行。

跳过错误最典型的方法是使用标题为"遇到数据一致性错误时继续"的分发代理程序配置文件。若要使用此配置文件,请右击分发代理程序,单击配置文件,然后选择此配置文件。然后,分发代理程序将跳过错误 2601、2627 和 20598。
*/




/*
实际上你写的事务应该可以这样理解为一个嵌套事务:
CREATE TABLE TestTran (Cola INT PRIMARY KEY, Colb CHAR(3))
GO
BEGIN TRANSACTION OuterTran -- @@TRANCOUNT set to 1.
GO
INSERT INTO TestTran VALUES (1, 'aaa')
GO
BEGIN TRANSACTION Inner1 -- @@TRANCOUNT set to 2.
GO
INSERT INTO TestTran VALUES (2, 'bbb')
GO
BEGIN TRANSACTION Inner2 -- @@TRANCOUNT set to 3.
GO
INSERT INTO TestTran VALUES (3, 'ccc')
GO
COMMIT TRANSACTION Inner2 -- Decrements @@TRANCOUNT to 2.
-- Nothing committed.
GO
COMMIT TRANSACTION Inner1 -- Decrements @@TRANCOUNT to 1.
-- Nothing committed.
GO
COMMIT TRANSACTION OuterTran -- Decrements @@TRANCOUNT to 0.
-- Commits outer transaction OuterTran.
GO

当在嵌套事务中使用时,内部事务的提交并不释放资源或使其修改成为永久修改。只有在提交了外部事务时,数据修改才具有永久性,而且资源才会被释放。当 @@TRANCOUNT 大于 1 时,每发出一个 COMMIT TRANSACTION 命令就会使 @@TRANCOUNT 按 1 递减。当 @@TRANCOUNT 最终减少到 0 时,将提交整个外部事务。因为 transaction_name 被 SQL Server 忽略,所以当存在仅将 @@TRANCOUNT 按 1 递减的显著内部事务时,发出一个引用外部事务名称的 COMMIT TRANSACTION。

当 @@TRANCOUNT 为 0 时发出 COMMIT TRANSACTION 将会导致出现错误,因为没有相应的 BEGIN TRANSACTION。

不能在发出一个 COMMIT TRANSACTION 语句之后回滚事务,因为数据修改已经成为数据库的一个永久部分。

当你把
INSERT INTO TestTran VALUES (3, 'ccc')
改成
INSERT INTO TestTran VALUES (1, 'ccc')
所影响的行数为 1 行)
(所影响的行数为 1 行)

服务器: 消息 2627,级别 14,状态 1,行 1
违反了 PRIMARY KEY 约束 'PK__TestTran__6A30C649'。不能在对象 'TestTran' 中插入重复键。
语句已终止。

发生的是2627错误,此时事务将会跳过错误,不会进行外层事务回滚。

当你把
INSERT INTO TestTran VALUES (3, 'ccc')
改成
INSERT INTO TestTran VALUES ('a', 'ccc')
服务器: 消息 245,级别 16,状态 1,行 1
将 varchar 值 'a' 转换为数据类型为 int 的列时发生语法错误。
服务器: 消息 3902,级别 16,状态 1,行 1
COMMIT TRANSACTION 请求没有对应的 BEGIN TRANSACTION。
服务器: 消息 3902,级别 16,状态 1,行 1
COMMIT TRANSACTION 请求没有对应的 BEGIN TRANSACTION。
服务器: 消息 3902,级别 16,状态 1,行 1
COMMIT TRANSACTION 请求没有对应的 BEGIN TRANSACTION。
外层事务回滚!

综上所述,事务是一个工作单元,但在某些情况下(发生错误 2601、2627 和 20598)时是个例外。

呵呵,三角分析的要是有什么差错还请高手指点!





rouqu 2004-08-25
  • 打赏
  • 举报
回复
所谓事务是用户定义的一个操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。事务具有四个特性:原子性!、一致性、隔离性和持续性。 --《数据库概论XX》
概念没有错,结合这个楼主可以再理解一下。
javaflyers 2004-08-24
  • 打赏
  • 举报
回复
: rouqu(石林#黄果树) ,你COPY的那些我也知道啊。

我想知道的是:如果事务中出现错误,会自动 回滚还是提交部分,我的例子好象 就是提交部分。
rouqu 2004-08-24
  • 打赏
  • 举报
回复
事务
事务是单个的工作单元。如果某一事务成功,则在该事务中进行的所有数据更改均会提交,成为数据库中的永久组成部分。如果事务遇到错误且必须取消或回滚,则所有数据更改均被清除。

Microsoft® SQL Server™ 以三种事务模式运行:

自动提交事务
每条单独的语句都是一个事务。

显式事务
每个事务均以 BEGIN TRANSACTION 语句显式开始,以 COMMIT 或 ROLLBACK 语句显式结束。

隐性事务
在前一个事务完成时新事务隐式启动,但每个事务仍以 COMMIT 或 ROLLBACK 语句显式完成。
rouqu 2004-08-24
  • 打赏
  • 举报
回复
CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3))
/*在这个事务内部,有个运行时错误,就是往主键中插入相同的值,但是事务不会回滚*/
begin tran
INSERT INTO TestBatch VALUES (1, 'aaa') --(所影响的行数为 1 行)
INSERT INTO TestBatch VALUES (2, 'bbb') --(所影响的行数为 1 行)
INSERT INTO TestBatch VALUES (1, 'ccc') /* 主键相同错误 */ --服务器: 消息 2627,级别 14,状态 1,行 1
--违反了 PRIMARY KEY 约束 'PK__TestBatch__22AA2996'。不能在对象 'TestBatch' 中插入重复键。语句已终止。
commit
SELECT * FROM TestBatch --(所影响的行数为 2 行)
javaflyers 2004-08-24
  • 打赏
  • 举报
回复
而且你的所写并不会回滚,提交 事务后,那个表中还是有2行数据。
javaflyers 2004-08-24
  • 打赏
  • 举报
回复
如果按照 hdhai9451(※★AC米兰☆※) 兄 的写法(要把最后的ENDIF改成END),事务一直得不到提交或者回滚,关闭文本窗口时才提示是否要提交事务。

再说,如果事务里每一句SQL都加上if @@error<>0 goto error_sign那不是太麻烦了
Andy__Huang 2004-08-24
  • 打赏
  • 举报
回复
CREATE TABLE TestBatch (Cola INT PRIMARY KEY, Colb CHAR(3))
begin tran
INSERT INTO TestBatch VALUES (1, 'aaa')
if @@error<>0 goto error_sign
INSERT INTO TestBatch VALUES (2, 'bbb')
if @@error<>0 goto error_sign
INSERT INTO TestBatch VALUES (1, 'ccc')
if @@error<>0 goto error_sign
commit tran
SELECT * FROM TestBatch


error_sign:
if @@error<>0
begin
rollback tran
select '事務提交失敗!'
endif

另外主鍵不能為空,也不能重復

27,579

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 应用实例
社区管理员
  • 应用实例社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧