c#软件开发学习笔记--事务、索引

SQL Server 事务与索引学习总结


一、事务(Transaction)

1.1 事务概念

事务是数据库操作的基本单位,确保一组操作要么全部成功执行,要么全部失败回滚。事务具有ACID特性:

  • 原子性(Atomicity):事务中的操作是不可分割的原子单元
  • 一致性(Consistency):事务执行前后数据完整性保持一致
  • 隔离性(Isolation):多个事务并发执行时互不干扰
  • 持久性(Durability):事务提交后数据永久保存

1.2 使用@@ERROR处理事务

通过检查@@ERROR全局变量来判断SQL执行是否出错:

sql 复制代码
declare @errorNum int -- 声明错误计数器
set @errorNum = 0

begin transaction -- 开始事务
begin
    -- 转出方扣款
    update CardNew set CurrentMoney = CurrentMoney - 1000 where StdudentId=1001
    set @errorNum = @errorNum + @@ERROR -- 记录当前错误号

    -- 转入方加款
    update CardNew set CurrentMoney = CurrentMoney + 1000 where StdudentId=1002
    set @errorNum = @errorNum + @@ERROR -- 记录当前错误号

    if(@errorNum > 0)
        begin
            rollback transaction -- 回滚事务
        end
    else
        commit transaction -- 提交事务
end

select * from CardNew

关键知识点

  • @@ERROR:返回上一条SQL语句的错误号,0表示成功
  • begin transaction:开始事务
  • commit transaction:提交事务(永久保存)
  • rollback transaction:回滚事务(撤销所有操作)

1.4 存储过程中的事务

将事务逻辑封装到存储过程中,提高复用性:

sql 复制代码
if exists(select * from sysobjects where name = 'Test1')
    drop proc Test1
go

create proc Test1
    @inAccount int,   -- 转入账户
    @outAccout int,   -- 转出账户
    @jine int         -- 转账金额
as
begin
    declare @errorNum int -- 错误计数器
    set @errorNum = 0

    begin transaction -- 开始事务
    begin
        -- 转出方扣款
        update CardNew set CurrentMoney = CurrentMoney - @jine where StdudentId=@outAccout
        set @errorNum = @errorNum + @@ERROR -- 记录错误

        -- 转入方加款
        update CardNew set CurrentMoney = CurrentMoney + @jine where StdudentId=@inAccount
        set @errorNum = @errorNum + @@ERROR -- 记录错误

        if(@errorNum > 0)
            rollback transaction -- 回滚事务
        else
            commit transaction -- 提交事务
    end
end
go

-- 调用存储过程进行转账
exec Test1 1000, 1003, 1000
select * from CardNew

1.5 使用TRY...CATCH处理事务

TRY...CATCH 是SQL Server 2005+引入的异常处理机制,比@@ERROR更简洁、更强大:

sql 复制代码
-- 使用TRY...CATCH实现事务
begin try
	begin transaction
		update t_cardnew set current_money = current_money - 1000 where stu_id = 1000;
		update t_cardnew set current_money = current_money + 1000 where stu_id = 1002;
	commit transaction
end try
begin catch
    if @@TRANCOUNT > 0 
		rollback transaction
end catch
go

TRY...CATCH工作流程

  1. BEGIN TRY块中的代码按顺序执行
  2. 如果TRY块中发生错误,程序立即跳转到BEGIN CATCH
  3. CATCH块中可以进行错误处理和事务回滚
  4. CATCH块执行完毕后,继续执行CATCH块之后的代码

1.6 存储过程中的TRY...CATCH事务

将TRY...CATCH事务逻辑封装到存储过程中:

sql 复制代码
if exists(select * from sys.objects where name = 'usp_test1')
    drop procedure usp_test1
go

create procedure usp_test1
    @InAccount int,   -- 转入账户
    @OutAccount int,  -- 转出账户
    @Amount int       -- 转账金额
as
begin
    begin try
    	begin transaction
    	
        update t_cardnew set current_money = current_money - @Amount where stu_id = @OutAccount;
        update t_cardnew set current_money = current_money + @Amount where stu_id = @InAccount;
        
        commit transaction
        print '转账成功'
    end try
    begin catch
    	if @@TRANCOUNT > 0 
        	rollback transaction
        print '转账失败'
        print error_message() -- 输出错误信息
    end catch
end
go

-- 调用存储过程进行转账
exec usp_test1 1000, 1002, 100

-- 查询转账结果
select * from t_cardnew;

关键知识点

  • ERROR_MESSAGE():返回当前错误的详细描述信息
  • TRY...CATCH模式更简洁,无需手动维护错误计数器
  • 错误发生时自动跳转到CATCH块,避免错误遗漏

二、索引(Index)

2.1 索引概述

索引是数据库中用于加速数据检索的数据结构,类似于书籍的目录。合理使用索引可以显著提高查询性能。

2.2 聚集索引 vs 非聚集索引

特性 聚集索引(Clustered) 非聚集索引(Non-clustered)
数据存储 数据按索引顺序物理存储 数据存储在单独位置,索引存储指向数据位置的指针
数量限制 每个表只能有一个 最多249个
唯一性 必须唯一(主键默认创建) 可以不唯一
适用场景 主键、经常排序的列 经常查询的非主键列

2.3 创建非聚集索引

sql 复制代码
-- 检查索引是否存在,如果存在则删除
if exists(select name from sysindexes where name = 'IX_Student_StudentName')
    drop index CardNew.IX_Student_StudentName
go

-- 创建非聚集索引
create nonclustered index IX_Student_StudentName
on CardNew(CurrentMoney)
with fillfactor = 30 -- 填充因子:索引页填充30%,预留70%空间供后续插入
go

关键知识点

  • fillfactor:填充因子,指定索引页的填充百分比,范围1-100
  • 填充因子越小,预留空间越多,减少页分裂,但占用更多存储空间
  • 填充因子越大,空间利用率越高,但插入/更新时可能导致页分裂

2.4 使用索引提示

强制查询使用指定的索引:

sql 复制代码
-- 使用索引提示强制使用指定索引
select * from CardNew
with (index=IX_CardNew_StudentName)
where StudentName like '张%'

-- 查询时强制使用指定索引
select * from CardNew
with (index=IX_Student_StudentName)

三、存储过程与分页

3.1 分页存储过程

使用OFFSET...FETCH NEXT实现分页查询:

sql 复制代码
if exists(select * from sysobjects where name = 'usp_test3')
    drop procedure usp_test3
go

create procedure usp_test3
    @pagesize int,           -- 每页大小
    @currentpage int,        -- 当前页码
    @where varchar(max),     -- 查询条件
    @totalPage int output    -- 输出参数:总页数
as
begin
    declare @sql1 nvarchar(max)   -- 查询数据的SQL
    declare @sql2 nvarchar(max)   -- 查询总数的SQL
    declare @total int
    declare @offset int

    -- 计算偏移量
    set @offset = (@currentpage - 1) * @pagesize

    -- 初始化SQL语句(N前缀表示Unicode字符串)
    set @sql1 = N'select * from StudentInfo where 1=1 '
    set @sql2 = N'select @count = count(*) from StudentInfo where 1=1 '

    -- 如果有查询条件,拼接条件
    if(len(trim(@where)) <> 0)
    begin
        set @sql1 += @where
        set @sql2 += @where
    end

    -- 拼接分页语句
    set @sql1 += N'order by Score offset ' + CAST(@offset as nvarchar) 
        + N' rows fetch next ' + CAST(@pagesize as nvarchar) + N' rows only';

    -- 执行查询数据的SQL
    exec(@sql1)

    -- 使用sp_executesql执行带输出参数的SQL
    exec sp_executesql @sql2, N'@count int output', @total output

    -- 计算总页数
    if(@total % @pagesize = 0)
        set @totalPage = @total / @pagesize
    else
        set @totalPage = @total / @pagesize + 1
end
go

-- 测试分页存储过程
declare @f1 int
exec usp_test3 3, 2, '', @f1 output
print @f1 -- 输出总页数

关键知识点

  • N'字符串':N前缀表示Unicode字符串(UTF-16)
  • OFFSET...FETCH NEXT:SQL Server 2012+支持的分页语法
  • sp_executesql:执行动态SQL,支持参数化查询,更安全
  • exec(@sql):执行动态SQL,但不支持参数化

3.2 增删改查存储过程

新增(Insert)

sql 复制代码
if exists(select * from sysobjects where name = 'p_stundentInfo_add')
    drop procedure p_stundentInfo_add
go

create procedure p_stundentInfo_add
    @studentName varchar(10),
    @score int
as
begin
    insert into StudentInfo(StudentName, Score) values(@studentName, @score)
end
go

exec p_stundentInfo_add '张三', 100

更新(Update)

sql 复制代码
if exists(select * from sys.procedures where name = 'p_studentinfo_update')
    drop proc p_studentinfo_update
go

create proc p_studentinfo_update
    @StudentId int,
    @StudentName varchar(50),
    @Score int
as
begin
    set nocount on; -- 关闭影响行数提示
    update StudentInfo 
    set StudentName = @StudentName, Score = @Score 
    where StudentId = @StudentId;
end
go

exec p_studentinfo_update 1002, '李四', 95

删除(Delete)

sql 复制代码
if exists(select * from sys.procedures where name = 'p_studentinfo_delete')
    drop proc p_studentinfo_delete
go

create proc p_studentinfo_delete
    @StudentId int
as
begin
    set nocount on;
    delete from StudentInfo where StudentId = @StudentId;
end
go

exec p_studentinfo_delete 1002

四、知识点总结

5.1 事务核心要点

  1. 事务四要素:原子性、一致性、隔离性、持久性
  2. 事务控制语句BEGIN TRANCOMMIT TRAN / ROLLBACK TRAN
  3. 错误检测@@ERROR(单条SQL错误)、TRY...CATCH(捕获异常)
  4. 事务状态@@TRANCOUNT(活动事务数)
  5. 错误抛出RAISERROR('消息', 16, 1)

5.2 索引核心要点

  1. 聚集索引:唯一、数据按索引顺序存储、每个表一个
  2. 非聚集索引:最多249个、索引和数据分开存储
  3. 填充因子fillfactor控制索引页填充比例
  4. 索引提示WITH (INDEX=索引名)强制使用指定索引

5.3 存储过程核心要点

  1. 分页实现OFFSET...FETCH NEXT语法
  2. 动态SQLexec(@sql)sp_executesql(推荐)
  3. 输出参数@参数名 类型 output
  4. Unicode字符串 :动态SQL使用N'字符串'前缀

相关推荐
geovindu1 小时前
CSharp: Composite Pattern
开发语言·后端·c#·组合模式·结构型模式
LeoTao1 小时前
C#专题-C# 异常处理
c#
宵时待雨1 小时前
linux笔记归纳9:进程间通信
linux·服务器·笔记
影寂ldy1 小时前
C# 多线程进阶知识点(线程优先级、多委托传参、线程锁、死锁)
开发语言·数据库·c#
疯狂打码的少年1 小时前
【软件工程】结构化设计:结构化设计方法(SC图)
笔记·软件工程
十月的皮皮1 小时前
C语言学习笔记20260716-编译与链接
c语言·笔记·学习
影视飓风TIM2 小时前
Linux基础入门笔记:命令、内核架构与压缩传输全梳理
linux·笔记·架构
私人珍藏库11 小时前
[Android] 会计快题库 -财会职称考试刷题学习
android·人工智能·学习·app·软件·多功能
chouchuang15 小时前
day-030-综合练习-笔记管理器
开发语言·笔记·python