1 Ø 事务 2 3 在数据库中有时候需要把多个步骤的指令当作一个整体来运行,这个整体要么全部成功,要么全部失败,这就需要用到事务。 4 5 1、 事务的特点 6 7 事务有若干条T-SQL指令组成,并且所有的指令昨晚一个整体提交给数据库系统,执行时,这组指令要么全部执行完成,要么全部取消。因此,事务是一个不可分割的逻辑单元。 8 9 10 11 事务有4个属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)以及持久性(Durability),也称作事务的ACID属性。 12 13 原子性:事务内的所有工作要么全部完成,要么全部不完成,不存在只有一部分完成的情况。 14 15 一致性:事务内的然后操作都不能违反数据库的然后约束或规则,事务完成时有内部数据结构都必须是正确的。 16 17 隔离性:事务直接是相互隔离的,如果有两个事务对同一个数据库进行操作,比如读取表数据。任何一个事务看到的所有内容要么是其他事务完成之前的状态,要么是其他事务完成之后的状态。一个事务不可能遇到另一个事务的中间状态。 18 19 持久性:事务完成之后,它对数据库系统的影响是持久的,即使是系统错误,重新启动系统后,该事务的结果依然存在。 20 21 22 23 2、 事务的模式 24 25 a、 显示事务 26 27 显示事务就是用户使用T-SQL明确的定义事务的开始(begin transaction)和提交(commit transaction)或回滚事务(rollback transaction) 28 29 b、 自动提交事务 30 31 自动提交事务是一种能够自动执行并能自动回滚事务,这种方式是T-SQL的默认事务方式。例如在删除一个表记录的时候,如果这条记录有主外键关系的时候,删除就会受主外键约束的影响,那么这个删除就会取消。 32 33 可以设置事务进入隐式方式:set implicit_transaction on; 34 35 c、 隐式事务 36 37 隐式事务是指当事务提交或回滚后,SQL Server自动开始事务。因此,隐式事务不需要使用begin transaction显示开始,只需直接失业提交事务或回滚事务的T-SQL语句即可。 38 39 使用时,需要设置set implicit_transaction on语句,将隐式事务模式打开,下一个语句会启动一个新的事物,再下一个语句又将启动一个新事务。 40 41 42 43 3、 事务处理 44 45 常用T-SQL事务语句: 46 47 a、 begin transaction语句 48 49 开始事务,而@@trancount全局变量用来记录事务的数目值加1,可以用@@error全局变量记录执行过程中的错误信息,如果没有错误可以直接提交事务,有错误可以回滚。 50 51 b、 commit transaction语句 52 53 回滚事务,表示一个隐式或显示的事务的结束,对数据库所做的修改正式生效。并将@@trancount的值减1; 54 55 c、 rollback transaction语句 56 57 回滚事务,执行rollback tran语句后,数据会回滚到begin tran的时候的状态 58 59 60 61 4、 事务的示例 62 63 --开始事务 64 begin transaction tran_bank; 65 declare @tran_error int; 66 set @tran_error = 0; 67 begin try 68 update bank set totalMoney = totalMoney - 10000 where userName = 'jack'; 69 set @tran_error = @tran_error + @@error; 70 update bank set totalMoney = totalMoney + 10000 where userName = 'jason'; 71 set @tran_error = @tran_error + @@error; 72 end try 73 begin catch 74 print '出现异常,错误编号:' + convert(varchar, error_number()) + ', 错误消息:' + error_message(); 75 set @tran_error = @tran_error + 1; 76 end catch 77 if (@tran_error > 0) 78 begin 79 --执行出错,回滚事务 80 rollback tran; 81 print '转账失败,取消交易'; 82 end 83 else 84 begin 85 --没有异常,提交事务 86 commit tran; 87 print '转账成功'; 88 end 89 go 90 91 92 93 Ø 异常 94 95 在程序中,有时候完成一些Transact-SQL会出现错误、异常信息。如果我们想自己处理这些异常信息的话,需要手动捕捉这些信息。那么我们可以利用try catch完成。 96 97 TRY…CATCH 构造包括两部分:一个 TRY 块和一个 CATCH 块。如果在 TRY 块中所包含的 Transact-SQL 语句中检测到错误条件,控制将被传递到 CATCH 块(可在此块中处理该错误)。 98 99 CATCH 块处理该异常错误后,控制将被传递到 END CATCH 语句后面的第一个 Transact-SQL 语句。如果 END CATCH 语句是存储过程或触发器中的最后一条语句,控制将返回到调用该存储过程或触发器的代码。将不执行 TRY 块中生成错误的语句后面的 Transact-SQL 语句。 100 101 如果 TRY 块中没有错误,控制将传递到关联的 END CATCH 语句后紧跟的语句。如果 END CATCH 语句是存储过程或触发器中的最后一条语句,控制将传递到调用该存储过程或触发器的语句。 102 103 TRY 块以 BEGIN TRY 语句开头,以 END TRY 语句结尾。在 BEGIN TRY 和 END TRY 语句之间可以指定一个或多个 Transact-SQL 语句。CATCH 块必须紧跟 TRY 块。CATCH 块以 BEGIN CATCH 语句开头,以 END CATCH 语句结尾。在 Transact-SQL 中,每个 TRY 块仅与一个 CATCH 块相关联。 104 105 # 错误函数 106 107 TRY...CATCH 使用错误函数来捕获错误信息。 108 ERROR_NUMBER() 返回错误号。 109 ERROR_MESSAGE() 返回错误消息的完整文本。此文本包括为任何可替换参数(如长度、对象名称或时间)提供的值。 110 ERROR_SEVERITY() 返回错误严重性。 111 ERROR_STATE() 返回错误状态号。 112 ERROR_LINE() 返回导致错误的例程中的行号。 113 ERROR_PROCEDURE() 返回出现错误的存储过程或触发器的名称。 114 115 116 117 118 119 120 示例 121 122 --错误消息存储过程 123 if (object_id('proc_error_info') is not null) 124 drop procedure proc_error_info 125 go 126 create proc proc_error_info 127 as 128 select 129 error_number() '错误编号', 130 error_message() '错误消息', 131 error_severity() '严重性', 132 error_state() '状态好', 133 error_line() '错误行号', 134 error_procedure() '错误对象(存储过程或触发器)名称'; 135 go 136 137 138 139 140 141 # 示例:用异常处理错误信息 142 143 --简单try catch示例 144 begin try 145 select 1 / 0; 146 end try 147 begin catch 148 exec proc_error_info; --调用错误消息存储过程 149 end catch 150 go 151 152 153 154 155 156 # 示例:异常能处理的错误信息 157 158 -- 159 --简单try catch示例,无法处理错误 160 begin try 161 select * * from student; 162 end try 163 begin catch 164 exec proc_error_info; 165 end catch 166 go 167 -- 168 --简单try catch示例,不处理错误(不存在的表对象) 169 begin try 170 select * from st; 171 end try 172 begin catch 173 exec proc_error_info; 174 end catch 175 go 176 -- 177 --异常处理,能处理存储过程(触发器)中(不存在表对象)的错误信息 178 if (object_id('proc_select') is not null) 179 drop procedure proc_select 180 go 181 create proc proc_select 182 as 183 select * from st; 184 go 185 begin try 186 exec proc_select; 187 end try 188 begin catch 189 exec proc_error_info; 190 end catch 191 go 192 193 194 195 196 197 198 199 200 201 异常不能处理编译期的错误,如语法错误。以及重编译造成部分名称对象得不到正确解析的时候所出现的错误。 202 203 204 205 # 示例:无法提交的事务 206 207 --创建临时用表 208 if (object_id('temp_tab', 'u') is not null) 209 drop table temp_tab 210 go 211 create table temp_tab( 212 id int primary key identity(100000, 1), 213 name varchar(200) 214 ) 215 go 216 217 begin try 218 begin tran; 219 --没有createTime字段 220 alter table temp_tab drop column createTime; 221 commit tran; 222 end try 223 begin catch 224 exec proc_error_info;--显示异常信息 225 if (xact_state() = -1) 226 begin 227 print '会话具有活动事务,但出现了致使事务被归类为无法提交的事务的错误。' 228 + '会话无法提交事务或回滚到保存点;它只能请求完全回滚事务。' 229 + '会话在回滚事务之前无法执行任何写操作。会话在回滚事务之前只能执行读操作。' 230 + '事务回滚之后,会话便可执行读写操作并可开始新的事务。'; 231 end 232 else if (xact_state() = 0) 233 begin 234 print '会话没有活动事务。'; 235 end 236 else if (xact_state() = 1) 237 begin 238 print '会话具有活动事务。会话可以执行任何操作,包括写入数据和提交事务。'; 239 end 240 end catch 241 go 242 243 244 245 246 247 # 示例:处理异常日志信息 248 249 -- 250 ---异常、错误信息表 251 if (object_id('errorLog', 'U') is not null) 252 drop table errorLog 253 go 254 create table errorLog( 255 errorLogID int primary key identity(100, 1), --ErrorLog 行的主键。 256 errorTime datetime default getDate(), --发生错误的日期和时间。 257 userName sysname default current_user, --执行发生错误的批处理的用户。 258 errorNumber int, --发生的错误的错误号。 259 errorSeverity int, --发生的错误的严重性。 260 errorState int, --发生的错误的状态号。 261 errorProcedure nvarchar(126), --发生错误的存储过程或触发器的名称。 262 errorLine int, --发生错误的行号。 263 errorMessage nvarchar(4000) 264 ) 265 go 266 -- 267 --存储过程:添加异常日志信息 268 if (object_id('proc_add_exception_log', 'p') is not null) 269 drop proc proc_add_exception_log 270 go 271 create proc proc_add_exception_log(@logId int = 0 output) 272 as 273 begin 274 set nocount on; 275 set @logId = 0; 276 begin try 277 if (error_number() is null) 278 return; 279 280 if (xact_state() = -1) 281 begin 282 print '会话具有活动事务,但出现了致使事务被归类为无法提交的事务的错误。' 283 + '会话无法提交事务或回滚到保存点;它只能请求完全回滚事务。' 284 + '会话在回滚事务之前无法执行任何写操作。会话在回滚事务之前只能执行读操作。' 285 + '事务回滚之后,会话便可执行读写操作并可开始新的事务。'; 286 end 287 else if (xact_state() = 0) 288 begin 289 print '会话没有活动事务。'; 290 end 291 else if (xact_state() = 1) 292 begin 293 print '会话具有活动事务。会话可以执行任何操作,包括写入数据和提交事务。'; 294 end 295 296 --添加日志信息 297 insert into errorLog values(getDate(), 298 current_user, error_number(), 299 error_severity(), error_state(), 300 error_procedure(), 301 error_line(), error_message()); 302 --设置自增值 303 select @logId = @@identity; 304 end try 305 begin catch 306 print '添加异常日志信息出现错误'; 307 exec proc_error_info;--显示错误信息 308 return -1; 309 end catch 310 end 311 go 312 -- 313 ---处理异常信息示例 314 declare @id int; 315 begin try 316 begin tran; 317 --删除带有外键的记录信息 318 delete classes where id = 1; 319 commit tran; 320 end try 321 begin catch 322 exec proc_error_info;--显示错误信息 323 if (xact_state() <> 0) 324 begin 325 rollback tran; 326 end 327 exec proc_add_exception_log @id output 328 end catch 329 select * from errorLog where errorLogID = @id; 330 go 331 332 333 334 335 Ø 游标 336 337 游标可以对一个select的结果集进行处理,或是不需要全部处理,就会返回一个对记录集进行处理之后的结果。 338 339 1、游标实际上是一种能从多条数据记录的结果集中每次提取一条记录的机制。游标可以完成: 340 341 # 允许定位到结果集中的特定行 342 343 # 从结果集的当前位置检索一行或多行数据 344 345 # 支持对结果集中当前位置的进行修改 346 347 由于游标是将记录集进行一条条的操作,所以这样给服务器增加负担,一般在操作复杂的结果集的情况下,才使用游标。SQL Server 2005有三种游标:T-SQL游标、API游标、客户端游标。 348 349 350 351 2、游标的基本操作 352 353 游标的基本操作有定义游标、打开游标、循环读取游标、关闭游标、删除游标。 354 355 A、 定义游标 356 357 declare cursor_name --游标名称 358 cursor [local | global] --全局、局部 359 [forward only | scroll] --游标滚动方式 360 [read_only | scroll_locks | optimistic] --读取方式 361 for select_statements --查询语句 362 [for update | of column_name ...] --修改字段 363 364 365 366 参数: 367 368 forward only | scroll:前一个参数,游标只能向后移动;后一个参数,游标可以随意移动 369 370 read_only:只读游标 371 372 scroll_locks:游标锁定,游标在读取时,数据库会将该记录锁定,以便游标完成对记录的操作 373 374 optimistic:该参数不会锁定游标;此时,如果记录被读入游标后,对游标进行更新或删除不会超过 375 376 377 378 B、 打开游标 379 380 open cursor_name; 381 382 游标打开后,可以使用全局变量@@cursor_rows显示读取记录条数 383 384 385 386 C、 检索游标 387 388 fetch cursor_name; 389 390 检索方式如下: 391 392 fetch first; 读取第一行 393 394 fetch next; 读取下一行 395 396 fetch prior; 读取上一行 397 398 fetch last; 读取最后一行 399 400 fetch absolute n; 读取某一行 401 402 如果n为正整数,则读取第n条记录 403 404 如果n为负数,则倒数提取第n条记录 405 406 如果n为,则不读取任何记录 407 408 fetch pelative n 409 410 如果n为正整数,则读取上次读取记录之后第n条记录 411 412 如果n为负数,则读取上次读取记录之前第n条记录 413 414 如果n为,则读取上次读取的记录 415 416 417 418 D、 关闭游标 419 420 close cursor_name; 421 422 423 424 E、 删除游标 425 426 deallocate cursor_name; 427 428 429 430 3、游标操作示例 431 432 --创建一个游标 433 declare cursor_stu cursor scroll for 434 select id, name, age from student; 435 --打开游标 436 open cursor_stu; 437 --存储读取的值 438 declare @id int, 439 @name nvarchar(20), 440 @age varchar(20); 441 --读取第一条记录 442 fetch first from cursor_stu into @id, @name, @age; 443 --循环读取游标记录 444 print '读取的数据如下:'; 445 --全局变量 446 while (@@fetch_status = 0) 447 begin 448 print '编号:' + convert(char(5), @id) + ', 名称:' + @name + ', 类型:' + @age; 449 --继续读取下一条记录 450 fetch next from cursor_stu into @id, @name, @age; 451 end 452 --关闭游标 453 close area_cursor; 454 455 --删除游标 456 --deallocate area_cursor;