SQL Server 事务、异常和游标

摘要:
您可以将事务设置为进入隐式模式:setimplicit_transactionon;c、 隐式事务隐式事务意味着SQL Server在事务提交或回滚后自动启动事务。b、 提交事务语句回滚事务,指示隐式或显式事务的结束,对数据库所做的更改将生效。在Transact-SQL中,每个TRY块仅与一个CATCH块关联。

Ø 事务

在数据库中有时候需要把多个步骤的指令当作一个整体来运行,这个整体要么全部成功,要么全部失败,这就需要用到事务。

1、 事务的特点

事务由若干条T-SQL指令组成,并且所有的指令昨晚一个整体提交给数据库系统,执行时,这组指令要么全部执行完成,要么全部取消。因此,事务是一个不可分割的逻辑单元。

事务有4个属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)以及持久性(Durability),也称作事务的ACID属性

原子性:事务内的所有工作要么全部完成,要么全部不完成,不存在只有一部分完成的情况。

一致性:事务内的然后操作都不能违反数据库的然后约束或规则,事务完成时有内部数据结构都必须是正确的。

隔离性:事务直接是相互隔离的,如果有两个事务对同一个数据库进行操作,比如读取表数据。任何一个事务看到的所有内容要么是其他事务完成之前的状态,要么是其他事务完成之后的状态。一个事务不可能遇到另一个事务的中间状态。

持久性:事务完成之后,它对数据库系统的影响是持久的,即使是系统错误,重新启动系统后,该事务的结果依然存在。

2、 事务的模式

a、 显式事务

显式事务就是用户使用T-SQL明确的定义事务的开始(begin transaction)和提交(commit transaction)或回滚事务(rollback transaction)

b、 自动提交事务

自动提交事务是一种能够自动执行并能自动回滚事务,这种方式是T-SQL的默认事务方式。例如在删除一个表记录的时候,如果这条记录有主外键关系的时候,删除就会受主外键约束的影响,那么这个删除就会取消。

可以设置事务进入隐式方式:set implicit_transaction on;

c、 隐式事务

隐式事务是指当事务提交或回滚后,SQL Server自动开始事务。因此,隐式事务不需要使用begin transaction显示开始,只需直接失业提交事务或回滚事务的T-SQL语句即可。

使用时,需要设置set implicit_transaction on语句,将隐式事务模式打开,下一个语句会启动一个新的事物,再下一个语句又将启动一个新事务。

3、 事务处理

常用T-SQL事务语句:

a、 begin transaction语句

开始事务,而@@trancount全局变量用来记录事务的数目值加1,可以用@@error全局变量记录执行过程中的错误信息,如果没有错误可以直接提交事务,有错误可以回滚。

b、 commit transaction语句

回滚事务,表示一个隐式或显示的事务的结束,对数据库所做的修改正式生效。并将@@trancount的值减1;

c、 rollback transaction语句

回滚事务,执行rollback tran语句后,数据会回滚到begin tran的时候的状态

4、 事务的示例

-- 开始事务
begin transaction tran_bank;
declare @tran_error int;    
set @tran_error = 0;    
begin try        
   update bank set totalMoney = totalMoney - 10000 where userName = 'jack';                
   set @tran_error = @tran_error + @@error;        
   update bank set totalMoney = totalMoney + 10000 where userName = 'jason';        
   set @tran_error = @tran_error + @@error;    
end try    
begin catch                
   print '出现异常,错误编号:' + convert(varchar, error_number()) + ', 错误消息:' + error_message();         
   set @tran_error = @tran_error + 1;    
end catch
if (@tran_error > 0)    
   begin     
      rollback tran;        
      print '转账失败,取消交易';    
   end
else    
   begin
      commit tran;        
      print '转账成功';    
   end
go

Ø 异常

在程序中,有时候完成一些Transact-SQL会出现错误、异常信息。如果我们想自己处理这些异常信息的话,需要手动捕捉这些信息。那么我们可以利用try catch完成。

TRY…CATCH 构造包括两部分:一个 TRY 块和一个 CATCH 块。如果在 TRY 块中所包含的 Transact-SQL 语句中检测到错误条件,控制将被传递到 CATCH 块(可在此块中处理该错误)。

CATCH 块处理该异常错误后,控制将被传递到 END CATCH 语句后面的第一个 Transact-SQL 语句。如果 END CATCH 语句是存储过程或触发器中的最后一条语句,控制将返回到调用该存储过程或触发器的代码。将不执行 TRY 块中生成错误的语句后面的 Transact-SQL 语句。

如果 TRY 块中没有错误,控制将传递到关联的 END CATCH 语句后紧跟的语句。如果 END CATCH 语句是存储过程或触发器中的最后一条语句,控制将传递到调用该存储过程或触发器的语句。

TRY 块以 BEGIN TRY 语句开头,以 END TRY 语句结尾。在 BEGIN TRY 和 END TRY 语句之间可以指定一个或多个 Transact-SQL 语句。CATCH 块必须紧跟 TRY 块。CATCH 块以 BEGIN CATCH 语句开头,以 END CATCH 语句结尾。在 Transact-SQL 中,每个 TRY 块仅与一个 CATCH 块相关联。

# 错误函数

TRY...CATCH 使用错误函数来捕获错误信息。
ERROR_NUMBER() 返回错误号。
ERROR_MESSAGE() 返回错误消息的完整文本。此文本包括为任何可替换参数(如长度、对象名称或时间)提供的值。
ERROR_SEVERITY() 返回错误严重性。
ERROR_STATE() 返回错误状态号。
ERROR_LINE() 返回导致错误的例程中的行号。
ERROR_PROCEDURE() 返回出现错误的存储过程或触发器的名称。
-- 错误消息存储过程
if (object_id('proc_error_info') is not null)    
   drop procedure proc_error_infogo
create proc proc_error_info
as    
   select         
   error_number() '错误编号',        
   error_message() '错误消息',        
   error_severity() '严重性',        
   error_state() '状态好',        
   error_line() '错误行号',        
   error_procedure() '错误对象(存储过程或触发器)名称';
go     
-- 用异常处理错误信息 
-- 简单 try catch 示例 ,不处理错误(不存在的表对象)
begin try    
  select 1 / 0;
end try
begin catch    
  exec proc_error_info; // 调用错误消息存储过程
end catch
go 
-- 简单try catch示例,不处理错误(不存在的表对象)
begin try    
  select * from st;
end try
begin catch    
  exec proc_error_info;
end catch
go
-- 异常处理,能处理存储过程(触发器)中(不存在表对象)的错误信息
if (object_id('proc_select') is not null)    
  drop procedure proc_selectgo
create proc proc_select
as    
select * from st;
go
begin try    
  exec proc_select;
end try
begin catch        
  exec proc_error_info;
end catch
go
-- 无法提交的事务
if (object_id('temp_tab', 'u') is not null)    
  drop table temp_tabgo
create table temp_tab(    
  id int primary key identity(100000, 1),    
  name varchar(200))
go
begin try    
  begin tran; // 没有 createTime 字段    
    alter table temp_tab drop column createTime;    
    commit tran;
end try
begin catch    
  exec proc_error_info; // 显示异常信息    
  if (xact_state() = -1)    
    begin        
      print '会话具有活动事务,但出现了致使事务被归类为无法提交的事务的错误。' 
      + '会话无法提交事务或回滚到保存点;它只能请求完全回滚事务。'            
      + '会话在回滚事务之前无法执行任何写操作。会话在回滚事务之前只能执行读操作。'            
      + '事务回滚之后,会话便可执行读写操作并可开始新的事务。';    
    end    
  else if (xact_state() = 0)    
    begin        
      print '会话没有活动事务。';    
    end    
  else if (xact_state() = 1)   
    begin        
      print '会话具有活动事务。会话可以执行任何操作,包括写入数据和提交事务。';    
    end
end catch
go
-- 处理异常日志信息 
-- 异常、错误信息表
if (object_id('errorLog', 'U') is not null)    
  drop table errorLoggo
create table errorLog(    
  errorLogID int primary key identity(100, 1), // ErrorLog 行的主键。    
  errorTime datetime default getDate(), // 发生错误的日期和时间。    
  userName sysname default current_user, // 执行发生错误的批处理的用户。    
  errorNumber int, // 发生的错误的错误号。    
  errorSeverity int, // 发生的错误的严重性。    
  errorState int, // 发生的错误的状态号。    
  errorProcedure nvarchar(126), // 发生错误的存储过程或触发器的名称。    
  errorLine int, // 发生错误的行号。    
  errorMessage nvarchar(4000))
go
-- 存储过程:添加异常日志信息
if (object_id('proc_add_exception_log', 'p') is not null)    
  drop proc proc_add_exception_log
go
create proc proc_add_exception_log(
  @logId int = 0 output)
as
begin    
  set nocount on;    
  set @logId = 0;    
  begin try        
    if (error_number() is null)            
      return;                
    if (xact_state() = -1)        
      begin            
        print '会话具有活动事务,但出现了致使事务被归类为无法提交的事务的错误。'                
        + '会话无法提交事务或回滚到保存点;它只能请求完全回滚事务。'                
        + '会话在回滚事务之前无法执行任何写操作。会话在回滚事务之前只能执行读操作。'                
        + '事务回滚之后,会话便可执行读写操作并可开始新的事务。';        
      end        
    else if (xact_state() = 0)        
      begin            
        print '会话没有活动事务。';        
      end        
    else if (xact_state() = 1)        
      begin            
        print '会话具有活动事务。会话可以执行任何操作,包括写入数据和提交事务。';        
      end                
      -- 添加日志信息        
      insert into errorLog values(getDate(), current_user, error_number(),error_severity(), error_state(), 
      error_procedure(), error_line(), error_message());        
      -- 设置自增值        
      select @logId = @@identity;    
  end try    
  begin catch        
    print '添加异常日志信息出现错误';        
    exec proc_error_info; // 显示错误信息        
    return -1;    
  end catch
end
go
-- 处理异常信息示例
declare @id int;
begin try    
  begin tran;    
  -- 删除带有外键的记录信息    
  delete classes where id = 1;    
  commit tran;
end try
begin catch    
  exec proc_error_info;
  -- 显示错误信息    
  if (xact_state() <> 0)    
    begin        
      rollback tran;    
    end    
  exec proc_add_exception_log @id outputend catch
select * from errorLog where errorLogID = @id;
go

Ø 游标

游标可以对一个select的结果集进行处理,或是不需要全部处理,就会返回一个对记录集进行处理之后的结果。

1、游标实际上是一种能从多条数据记录的结果集中每次提取一条记录的机制。游标可以完成:

# 允许定位到结果集中的特定行

# 从结果集的当前位置检索一行或多行数据

# 支持对结果集中当前位置的行进行修改

由于游标是将记录集进行一条条的操作,所以这样给服务器增加负担,一般在操作复杂的结果集的情况下,才使用游标。SQL Server 2005有三种游标:T-SQL游标、API游标、客户端游标。

2、游标的基本操作

游标的基本操作有定义游标、打开游标、循环读取游标、关闭游标、删除游标。

-- A 定义游标的语法
declare cursor_name // 游标名称
cursor [local | global] // 全局、局部
[forward only | scroll] // 游标滚动方式
[read_only | scroll_locks | optimistic] // 读取方式
for select_statements // 查询语句
[for update | of column_name ...] // 修改字段     
-- 参数解释: 
forward only | scroll // only: 游标只能向后移动; scroll: 游标可以随意移动 
-- read_only:只读游标 
-- scroll_locks:游标锁定,游标在读取时,数据库会将该记录锁定,以便游标完成对记录的操作 
-- optimistic:该参数不会锁定游标
-- B 打开游标 
open cursor_name; // 游标打开后,可以使用全局变量 @@cursor_rows 显示读取记录条数 
-- C 检索游标 
fetch cursor_name; 
-- 检索方式如下: 
fetch first; // 读取第一行 
fetch next; // 读取下一行 
fetch prior; // 读取上一行 
fetch last; // 读取最后一行 
fetch absolute n; // 读取某一行 如果n为正整数,则读取第n条记录,如果n为负数,则倒数提取第n条记录 
fetch pelative n; // 如果n为正整数,则读取上次读取记录之后第n条记录,如果n为负数,则读取上次读取记录之前第n条记录 
-- D 关闭游标 
close cursor_name; 
-- E 删除游标 
deallocate cursor_name; 
/* 游标操作示例 */
declare cursor_stu cursor scroll 
for 
  select id, name, age from student;
open cursor_stu;
declare @id int,        
@name nvarchar(20),        
@age varchar(20);
fetch first from cursor_stu into @id, @name, @age; // 读取第一条记录
print '读取的数据如下:';
-- 全局变量
while (@@fetch_status = 0)
  begin    
    print '编号:' + convert(char(5), @id) + ', 名称:' + @name + ', 类型:' + @age;    
    -- 继续读取下一条记录    
    fetch next from cursor_stu into @id, @name, @age;
  end
close area_cursor;
deallocate area_cursor;

免责声明:文章转载自《SQL Server 事务、异常和游标》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【python】flask+nginx配置基于Vue实现拖拽效果及阻止拖拽下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

O/R Mapping 工具介绍 LLBLgen控件(转)

LLBLgen控件 ,可以使用Google搜索到他的官方网站,下载最新试用版,另完全破解国内版本还没有出现.最新的是2.6版,内容不断更新中. 声明:部分资源来源于网络,具体地址实在想不起来,英文代码基本上是从google搜索的,国内很少有这个工具的帮助,如果真的是抄袭勿怪! 同时,欢迎转载! 软件使用: root namespace 是指定的编译成p...

【SQL server初级】数据库性能优化三:程序操作优化

  数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第三部分   数据库性能优化三:程序操作优化 概述:程序访问优化也可以认为是访问SQL语句的优化,一个好的SQL语句是可以减少非常多的程序性能的,下面列出常用错误习惯,并且提出相应的解决方案     一、操作符优化   1. IN、NOT IN 操作符   IN和EXIS...

Mybatis中 ResultMap的collection配置一对多使用详解

resultMap可以将查询到的多种数据,映射到一个符合要求的对象上。   1.实现操作的基本流程     调用Mapper接口的方法→→Mapper.xml文件中对应id的查询语句(接口的方法名=查询语句的id)→查询语句配置resultMap属性(属性值是其对应ResultMap标签的id)→在resultMap标签中,配置查到的表的字段与对象的属性的...

Navicat 创建mysql存过、定时执行存过

创建存过:使用Navicat for MySQL工具创建存储过程步骤: 1. 新建函数(选择函数标签 -> 点击新建函数):   2.输入函数的参数个数、参数名、参数类型等: 3.编写存储过程:  代码如下: Sql代码   BEGIN    /* 定义变量 */   declare tmp0 VARCHAR(1000);   decla...

Hadoop源码分析5: RPC基本线程

1. 数据记录FileStatus public class FileStatus implements Writable {       private String filename;        private long time;        static {  // register IPCFileStatus        Writab...

微信小程序创建自定义select组件(内含组件与父页面的交互流程)

 闲来无事将制作的select自定义组件制作贴出来,供大家借鉴,也可以为了以后的使用做个备注。 select是我自定义的组件,这个一定要记住,它是自定义组件! 以下书写select组件代码 其中的wxml如下 <view class="select_wrap"> <!-- 左边文字 --> <view class=...