第三阶段数据库-10:存储过程,事务,异常,视图,自定义函数,触发器,N关键字
1_存储过程
(1)存储过程就是封装一段SQL语句,让SQL语句当成一个整体(编译过),可以直接调用整体,提高性能
(2)特点:
类似于C#中方法。方法名,方法参数列表,返回值,输入参数in,输出参数out
存储过程也有参数,也有返回值,输入参数,输出参数
--1.判断某个存储过程是否存在 如果已经存在 先删除 再创建
if exists (select * FROM sys.procedures where name ='p_studentinfo_insert')
drop procedure p_studentinfo_insert
go
-- 2.定义存储过程 Procedure 缩写 proc
create proc p_studentinfo_insert--参数列表,定义格式 参数名称 参数类型 每个参数以英文逗号隔开,最后一个参数不加逗号--输入参数(类似于C#中形式参数)@StudentName varchar(50),@Score int,--输出参数: 必须赋值, 标志名:output@ReturnValue int output
as
begin--begin 和 end 之间存储的是业务逻辑,将来业务逻辑可以是复杂的业务逻辑insert into StudentInfo(StudentName,Score) values(@StudentName,@Score);--@@rowcount; sql执行时影响的行数set @ReturnValue= @@ROWCOUNT;return 100;
end
go
-- 在 sql server 中调用存储过程
-- execute 简写 exec
declare @rv int-- 接收输出参数,受影响的行数
declare @return int-- 接收输出参数,return出的100
exec @return = p_studentinfo_insert '吴亦凡666',82,@rv output
print @rv;
print @return;
(3)在C#中调用存储过程
//1.拼接存储过程使用的where条件
string where = string.Empty;
if (!string.IsNullOrWhiteSpace(txtStudentName.Text.Trim()))
{where += $" and StudentName like '%{txtStudentName.Text.Trim()}%'";
}
if (nudScoreStart.Value > 0)
{where += $" and Score >={nudScoreStart.Value}";
}
if (nudScoreEnd.Value > 0)
{where += $" and Score <={nudScoreEnd.Value}";
}
//2.设置参数
SqlParameter[] sqlParameters = new SqlParameter[]
{new SqlParameter("@currentPage",SqlDbType.Int),new SqlParameter("@pageSize",SqlDbType.Int),new SqlParameter("@where",SqlDbType.VarChar),new SqlParameter("@totalPage",SqlDbType.Int),
};
//输入参数
sqlParameters[0].Value = CurrentPage;
sqlParameters[1].Value = PageSize;
sqlParameters[2].Value = where;
//输出参数 不需要赋值
sqlParameters[3].Direction = ParameterDirection.Output;
//3.调用存储过程
//CommandType.StoredProcedure 存储过程
DataSet ds = SqlHelper.ExecuteDataset(connectionString, CommandType.StoredProcedure, "p_studentinfo_select", sqlParameters);
//4.绑定数据源
dataGridView1.DataSource = ds.Tables[0];
ToTalPage = Convert.ToInt32(sqlParameters[3].Value);
lblPageAndTotalPage.Text = $"{CurrentPage} / {ToTalPage}";
2_事务
(1)事务:是一个整体,多个SQL语句在一个整体内完成,比如:一个整体中包含一个查询,一个更新, 一个删除语句,事务可以保证这三个语句要么同时成功,要么同时失败。不会出现一部分成功,一部分失败的情况。事务保证的是数据的一致性。
(2)raiserror和 throw都是用于抛出异常和生成错误消息的函数
@@error 语句的错误编号。若语句执行成功,返回0
--1.开始事务 事务把很多的业务逻辑当成一个整体来执行
begin transaction
begininsert into StudentInfo(StudentName,Score) values ('吴亦凡123',97);insert into StudentInfo(StudentName,Score) values ('吴亦凡456',99);update StudentInfo set StudentName='吴亦凡666',Score=12 where StudentId = 20;--,raiserror 和 throw 都是用于抛出异常和生成错误消息的函数 raiserror('发生了一个错误',16,1);
end
-- != <> 不等于
-- @@error 语句的错误编号。若语句执行成功,返回0;
if @@error <> 0
begin rollback tran --2.回滚事务(失败)print '执行事务失败'
end
else
begin commit tran --3.提交事务)成功print '执行事务成功'
end
(3)语法如下: 开始事务:使用BEGIN TRANSACTION或BEGIN TRANSACTION。 提交事务:使用COMMIT TRANSACTION。 回滚事务:使用ROLLBACK TRANSACTION。
(4)事务的隔离级别(了解) SQL Server提供了多种事务隔离级别,用于控制并发事务之间的可见性和锁行为。常见的隔离级别包括: READ COMMITTED:默认级别,只能读取已提交的数据 READ UNCOMMITTED:允许读取未提交的数据(脏读) REPEATABLE READ:保证在事务中多次读取同一数据时结果一致 SERIALIZABLE:最高隔离级别,事务完全隔离,避免并发问题
3_捕获异常
begin trydeclare @i int;set @i = cast('abc' as int);print @i
end try
begin catchprint '转换失败'
end catch
--事务中捕获异常
begin tran
begin trydelete from StudentInfo where StudentId =10;raiserror('一个错误',12,1);print '全部逻辑成功,就提交';commit tran
end try
begin catchprint '任意一个逻辑失败,就回滚'rollback tran
end catch
4_视图
(1)相当于时一个 '临时表' 就是虚拟表 它时由多个表组成的结果集,平时建议只对视图进行查询操作,增 删 改 应该应用真实的表结构
--视图 view
if exists (select table_name from infomation_schema.views where table_name = N'v_studentinfo')
drop View v_studentinfo
go
create view v_customerInfo
as
select C.CustomerId,C.CustomerName,case C.Sex when 1 then '男' else '女' end as SexName1,case C.Sex when 1 then '男' when 0 then '女'else '未知' end as SexName2,
C.Age,C.Phone,
A.ProvinceName+A.City+A.Area+A.DetailAddress as AddressDetail
from CustomerInfo as C left join AddressInfo as A
on C.AddressId=A.AddressId
go
--查询视图
select * from v_studentinfo;
5_自定义函数
(1)示例
-- 自定义函数
if OBJECT_ID('dbo.f_add', 'FN') IS NOT NULLdrop function f_add
go
create function f_add(@x int , @y int)
returns int--返回值类型
as
beginreturn @x+@y;
end
go
--调用自定义函数
declare @r int;
-- database owner 数据库拥有者
select @r = dbo.f_add(10,10)
print @r;
(2)一些内置的函数
select convert(varchar(10),getdate(),121)
select dateadd(month, 1, '20240830');
select dateadd(year, 1, getdate());
select power(2,3)
select ceiling(2.1)
select floor(2.9)
select round(4.6,0,0)
SELECT ROUND(123.4545, 2), ROUND(163.45, -2);
SELECT ROUND(150.75, 0);
SELECT ROUND(150.75, 0, 2);
select left('hello',3)
select right('hello',3)
--截取,查索引,合并(串联),转换大写,转换小写,去空白,格式化,替换。
select substring('hello',1,3)
select upper('hello');
select lower('HELLO');
select ltrim(' hello')
select rtrim('hello ')
select trim(' hello ')
select reverse('hello');
select char(1)
select char( ascii('a') )
select 'abc'+'def'
select concat('hello',' world','how',' are',' you')
6_触发器
if exists (select * from sys.triggers where name = 'tri_studentinfo_insert')
drop trigger tri_studentinfo_insert;
create trigger tri_studentinfo_insert on StudentInfo for insert
as
print '我是向StudenInfo中插入数据的时候执行的触发器'
go
update StudentInfo set StudentName = 'ABC' where StudentName='张三69'
insert into StudentInfo (StudentName,Score) values ('abc',90);
7_N关键字
(1)在 SQL Server 中,N 前缀用于表示其后跟的字符串是一个 Unicode 字符串字面量(nvarchar 类型),而不是普通的非 Unicode 字符串字面量(varchar 类型)。
'Some text' --普通字符串,类型为 varchar N'Some text' --Unicode 字符串,类型为 nvarchar
(2)需要N 前缀的原因?关键区别
varvhar 和 nvarchar 的根本区别在于字符编码和存储方式:
特性 | VARCHAR (非 Unicode) | NVARCHAR (Unicode) |
---|---|---|
编码方式 | 使用数据库的代码页(如 CP1252, GB2312) | 使用 Unicode 标准(UCS-2/UTF-16) |
存储空间 | 每个字符占 1-2 字节(取决于代码页) | 每个字符占 2 字节(基本多语言平面) |
字符支持 | 仅限于特定代码页定义的字符集 | 支持全球几乎所有语言的字符 |
最大长度 | VARCHAR(n) :最多 8,000 字符 | NVARCHAR(n) :最多 4,000 字符 |
(3)N前缀的使用
当您需要处理非英文字符(如中文、日文、阿拉伯文等)或需要确保字符跨系统兼容时,必须使用 N前缀
假设有一个存储用户名的表:
CREATE TABLE Users ( Id INT IDENTITY PRIMARY KEY, UserName NVARCHAR(50) -- 定义为 Unicode 列
);
情况一:错误写法(不使用 N
):这里 '张三' 作为 VARCHAR
字符串处理,如果数据库代码页不支持中文,会导致乱码(如 ??
)。
INSERT INTO Users (UserName) VALUES ('张三');
情况二:正确写法(使用 N
)
INSERT INTO Users (UserName) VALUES (N'张三');
-- 正确插入中文字符
INSERT INTO Users (UserName) VALUES (N'すばらしい');
-- 正确插入日文字符
INSERT INTO Users (UserName) VALUES (N'محمد');
-- 正确插入阿拉伯文字符
情况三:查询时的比较
-- 可能无法正确匹配
SELECT * FROM Users WHERE UserName = '张三';
-- 正确的 Unicode 字符串比较
SELECT * FROM Users WHERE UserName = N'张三';
(4)其他使用场景
A. 存储过程参数
CREATE PROCEDURE AddUser@Name NVARCHAR(50)
AS
BEGININSERT INTO Users (UserName) VALUES (@Name);
END
-- 调用时也需要使用 N 前缀
EXEC AddUser @Name = N'李四';
B. 函数参数
SELECT * FROM Users
WHERE UserName LIKE N'张%'; -- 使用 Unicode 模式匹配
C. 变量声明
DECLARE @MyName NVARCHAR(50) = N'王五';