SQL Server 事务、异常和游标

建议先阅读存储过程:SQL Server 存储过程

? 事务

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

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_info

go

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 student;

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_select

go

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_tab

go

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 errorLog

go

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 output

end 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:前一个参数,游标只能向后移动;后一个参数,游标可以随意移动

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条记录

如果n为,则不读取任何记录

fetch pelative n

如果n为正整数,则读取上次读取记录之后第n条记录

如果n为负数,则读取上次读取记录之前第n条记录

如果n为,则读取上次读取的记录

D、 关闭游标

close cursor_name;

E、 删除游标

deallocate cursor_name;

3、游标操作示例

--创建一个游标
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;

时间: 2024-10-13 00:19:36

SQL Server 事务、异常和游标的相关文章

Sql server存储过程中常见游标循环用法

原文:Sql server存储过程中常见游标循环用法 用游标,和WHILE可以遍历您的查询中的每一条记录并将要求的字段传给变量进行相应的处理 DECLARE @A1 VARCHAR(10), @A2 VARCHAR(10), @A3 INT DECLARE YOUCURNAME CURSOR FOR SELECT A1,A2,A3 FROM YOUTABLENAME OPEN YOUCURNAME fetch next from youcurname into @a1,@a2,@a3 while

SQL Server事务回滚对自增键的影响

SQL Server事务回滚时是删除原先插入导致的自增值,也就是回滚之前你你插入一条数据导致自增键加1,回滚之后还是加1的状态 --如果获取当前操作最后插入的identity列的值:select @@IDENTITY--如果要获取某表的最后的identity列的值:select IDENT_CURRENT('表名') --如果要模拟抛出异常可以用RAISERROR --RAISERROR('错误的描述',错误的严重级别代码,错误的标识,错误的描述中的参数的值(这个可以是多个),一些其它参数) -

SQL Server 事务隔离级别详解

原文:SQL Server 事务隔离级别详解 标签: SQL SEERVER/MSSQL SERVER/SQL/事务隔离级别选项/设计数据库事务级别 SQL 事务隔离级别 概述 隔离级别用于决定如果控制并发用户如何读写数据的操作,同时对性能也有一定的影响作用. 步骤 事务隔离级别通过影响读操作来间接地影响写操作:可以在回话级别上设置事务隔离级别也可以在查询(表级别)级别上设置事务隔离级别.事务隔离级别总共有6个隔离级别:READ UNCOMMITTED(未提交读,读脏),相当于(NOLOCK)R

Sql Server 事务日志

Sql Server事务日志文件是数据库文件的重要组成部分,事务日志主要用来存放数据库的修改记录.数据库为了得到更高的写入效率和性能,同时保证ACID特性,数据在写入时,会将更新先写入事务日志,因为事务日志是连写的,所以写事务会比较快.简单来说,顺序写入时,磁盘的磁头会保持在一定的区域内连续写入,而数据写入数据文件时,有随机性,磁盘的磁头移动消耗的时间要比数据写入日志文件时多. Sql Server对于事务日志文件的管理,是将日志文件在逻辑上分成若干个文件(VLFS),方便管理. 创建一个1M的

如何处理SQL Server事务复制中的大事务操作

如何处理SQL Server事务复制中的大事务操作 事务复制的工作机制 事务复制是由 SQL Server 快照代理.日志读取器代理和分发代理实现的.快照代理准备快照文件(其中包含了已发布表和数据库对象的架构和数据),然后将这些文件存储在快照文件夹中,并在分发服务器中的分发数据库中记录同步作业. 日志读取器代理监视为事务复制配置的每个数据库的事务日志,并将标记为要复制的事务从事务日志复制到分发数据库中,分发数据库的作用相当于一个可靠的存储-转发队列. 分发代理将快照文件夹中的初始快照文件和分发数

SQL Server事务执行一半出错是否自动回滚整个事务 【转】

http://www.2cto.com/database/201308/234728.html SQL Server事务执行一半出错是否自动回滚整个事务 大家都知道SQL Server事务是单个的工作单元.如果某一事务成功,则在该事务中进行的所有数据修改均会提交,成为数据库中的永久组成部分.如果事务遇到错误且必须取消或回滚,则所有数据修改均被清除. 所以是不是说事务出错一定会回滚整个事物呢? 先看几个个例子: --createtable create table testrollback(idi

监控SQL Server数据库异常镜像状态发告警邮件

监控SQL Server数据库异常镜像状态发告警邮件 在部署了数据库镜像之后,我们需要监控参与镜像的主数据库和镜像数据库的状态,如果状态异常,发送告警邮件.那么这个脚本需要在主和镜像服务器上都运行. 目录视图sys.database_mirroring对SQL Server实例上的每个数据库都包含一行(包括系统数据库和未配置镜像的数据库),当然也包含所有镜像数据库的状态信息.我们可以查询该目录视图,对于每个异常状态的镜像数据库触发告警邮件.笔者的环境配置的是异步镜像,依赖于手动故障转移. 前提条

SQL Server事务日志分析

SQL Server事务日志分析 fn_dblog()和fn_dump_dblog()函数介绍 SQL Server有两个未公开的函数fn_dblog()和fn_dump_dblog()非常有用并且提供的信息量很大.你可以使用这些函数来获取100多列大量的有用信息. fn_dblog()用于分析数据库当前的事务日志文件,它需要两个参数,分别为事务开始LSN和结束LSN,默认为NULL,表示返回事务日志文件的所有日志记录. 例如: SELECT * FROM fn_dblog(null,null)

SQL Server 事务嵌套

原文:SQL Server 事务嵌套 示例代码: DECLARE @TranCounter INT; SET @TranCounter = @@TRANCOUNT; IF @TranCounter > 0 -- Procedure called when there is -- an active transaction. -- Create a savepoint to be able -- to roll back only the work done -- in the procedure

理解Sql Server 事务隔离层级(Transaction Isolation Level)

关于Sql Server 事务隔离级别,百度百科是这样描述的 隔离级别:一个事务必须与由其他事务进行的资源或数据更改相隔离的程度.隔离级别从允许的并发副作用(例如,脏读或虚拟读取)的角度进行描述. 隔离级别共5种: read uncommitted | 0 未提交读read committed | 1 已提交读repeatable read | 2 可重复读serializable | 3 可序列化snapshot 快照(2005版本以后新加) 以下面的图为例,在事务A中会根据条件读取TABLE