数据库工程开发秘籍之TSQL 存储过程user stored procedure的概念与案例实战
Posted ShenLiang2025
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库工程开发秘籍之TSQL 存储过程user stored procedure的概念与案例实战相关的知识,希望对你有一定的参考价值。
TSQL存储过程概念与案例实战
存储过程
存储过程由一个或多个 T-SQL 语句或对.NET公共语言运行时 (CLR) 方法的引用所构成的一组程序块。这里的T-SQL语句包括执行DDL、DML语句、应用临时表、动态SQL、定义异常处理等。但是相比于函数,它不能嵌套在查询里,但它可以调用其它的存储过程,即存储过程可以相互调用。
存储过程和常见的程序语言类似,可以指定输入和输出参数。SQL Server通过缓存存储过程的执行计划进而达到节约时间、降低CPU、内存的目的。
存储过程相对于在应用端实现业务逻辑有以下好处:
1 通过封装实现重用性和逻辑复杂性的隐藏。仅需要通过存储过程的修改(alter procedure)就能应用新的逻辑。
2 减少网络的传输,这是因为存储过程即存储在数据库里,而如果是应用程序方式访问数据库识别有网络传输的成本。
3 存储过程可以提供对数据库进行安全的访问,即赋予了执行存储过程的用户不能直接访问底层的数据库对象,这种隔离对数据库提供了保障。
补注:尽管存储过程有这些优点,但实际应用程序封装在Service(服务层),其对应的是某些单独的SQL逻辑,而如果都放在存储过程则会出现灵活性差、代码难维护的情况。
普通存储过程
假设我们有一张tb_user表,有两个字段id,name,如果我想通过传参数的形式给这张表插入数据,那么我们可以通过存储过程来实现。这里id需要沿着tb_user的最大的值往下自增,自增间隔是1。
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[usp_useradd_MS]
(@username VARCHAR(100))
AS
DECLARE @max_id int=0
DECLARE @has int=0
BEGIN
SELECT @max_id=max(id) FROM tb_user ;
SELECT @has=1 FROM tb_user WHERE name = @username;
IF @has =0
BEGIN
INSERT INTO tb_user VALUES(@max_id+1,@username);
END;
END;
GO
--调用,可选用下列任一方式执行
exec usp_useradd_MS 'Mike' exec usp_useradd_MS @username='John'
--验证结果
SELECT * FROM tb_user;
存储过程与动态SQL
所谓动态SQL即SQL的内容是灵活的,是通过字符串拼接出来的,可以理解是不固定的。比如有一个需求,我们想通过查询指定表的字段名并以列的形式展示出来,这里就可以通过存储过程结合动态SQL来实现。因为每次传过来的参数即表名是不固定的,所以需要一个字符串变量拿到参数里的值再拼接成最终的sql(相对于翻译一下),再去数据库里执行。
CREATE PROCEDURE [dbo].[usp_getColumnsBycolumn]( @tabname VARCHAR(100))
AS
DECLARE @sql VARCHAR(8000)
DECLARE @STRING VARCHAR(500)
BEGIN
SELECT @sql= ISNULL(@sql+',','')+'['+CAST(COLID AS VARCHAR(12))+']'
FROM SYSCOLUMNS
WHERE ID = OBJECT_ID(@tabname)
GROUP BY COLID
SET @STRING='SELECT * FROM (SELECT NAME,COLID FROM SYSCOLUMNS WHERE ID=OBJECT_ID('''+@tabname+'''))A pivot (MAX(NAME) for COLID in('+@sql+'))t'
EXEC(@STRING)
END
GO
--调用及结果
exec usp_getColumnsBycolumn 'tb_user'
存储过程内调用存储过程
存储过程里是可以调用其它存储过程的,有时一些复杂的逻辑需要多个存储过程结合一起才能实现最终效果。
CREATE PROCEDURE [dbo].[usp_getColumnsByonecolumn]( @tabname VARCHAR(100))
AS
DECLARE @tab Table (colid varchar(100),colname varchar(20))
BEGIN
INSERT INTO @tab exec usp_getColumnsBycolumn @tabname
SELECT colid FROM @tab
END
--调用
exec usp_getColumnsByonecolumn 'tb_user'
-- 结果
注意:我们不可以把存储过程包裹在子查询里,即如下方式查询存储过程的结果是不支持的。
SELECT * FROM(exec usp_getColumnsBycolumn 'tb_user')A
系统函数查看存储过程的定义
可以通过内置的系统函数sp_helptext查看存储过程的文本定义。
sp_helptext @objname = N'usp_getColumnsByonecolumn'
#结果,存储过程的文本定义见下:
CREATE PROCEDURE [dbo].[usp_getColumnsByonecolumn]( @tabname VARCHAR(100))
AS
DECLARE @tab Table
(
colid varchar(100),
colname varchar(20)
)
BEGIN
INSERT INTO @tab exec usp_getColumnsBycolumn @tabname
SELECT colid FROM @tab
END
存储过程的安全性
存储过程跟表、视图一样首先用户和角色,赋予EXECUTE权限后才能执行。特别的,如果过程里用到非dbo的schema(架构)那么在赋予存储过程权限后也要将该架构下表的访问权限赋予用户,否则存储可以执行但是会报底层表访问权限的错误。
CREATE LOGIN db_loginuser WITH PASSWORD = 'D#2$3)5b';
GO CREATE USER db_user FOR LOGIN db_loginuser;
GO EXECUTE AS LOGIN = 'db_loginuser';
SELECT SUSER_NAME() AS [login], USER_NAME() AS [user];
exec usp_useradd_MS 'Alice'
REVERT;
SELECT SUSER_NAME() AS [login], USER_NAME() AS [user];
GRANT EXEC ON dbo.usp_useradd_MS TO db_user;
EXECUTE AS LOGIN = 'db_loginuser';
SELECT SUSER_NAME() AS [login], USER_NAME() AS [user];
exec usp_useradd_MS 'Alice'
REVERT;
SELECT * FROM tb_user
注:针对sa、dbo无法执行,即无法通过下列的语句切换到sa用户
EXECUTE AS LOGIN = 'sa';
EXECUTE AS LOGIN = 'dbo';
可通过REVERT;语句切换到上一次的用户执行环境。
存储过程与事务
事务是一个工作整体,它包含一个或多个操作数据(可能还包括数据结构)的活动。比如我们建表的DDL语句就是一个事务,要么建表成功要么失败,不存在中间状态。事务具有ACID性,其中A(atomicity)即原子性、C(consistency)即一致性、I(isolation)隔离性、D(durability)持久性。
SQL Server事务分为隐性事务和显性事务的,如果是隐性的可通过SET IMPLICIT_TRANSACTIONS ON;开关打开该选项,在该模式下只需要在要操作的事务的尾部编写commit或者rollback即可提交或者回滚事务,而不用在事务开头写begin tran。相对的显式事务需要在事务前后定义诸如BEGIN TRAN … COMMIT TRAN(ROLLBACK TRAN)
隐式事务的例子:
SET IMPLICIT_TRANSACTIONS ON
select @@TRANCOUNT
INSERT INTO tb_user VALUES(110,'Philips')
select @@TRANCOUNT
-- 注:以上代码需要一起执行。
-- 查看插入的数据
SELECT * FROM tb_user A WHERE A.name = 'Philips'
--这里如果回滚则数据不存在。
ROLLBACK
以回滚为例演示在存储过程里如何使用事务。如果传入的参数(用户名)是'Lily'则回滚,其它则插入并提交事务。
CREATE PROCEDURE [dbo].[usp_useradd_MS_trans](@username VARCHAR(100))
AS
BEGIN
print @username;
if @username='Lily'
BEGIN
BEGIN TRAN
exec usp_useradd_MS @username
ROLLBACK TRAN
END
ELSE
BEGIN
exec usp_useradd_MS @username
END
END
GO
-- 执行与结果
EXEC usp_useradd_MS_trans 'Lily'
EXEC usp_useradd_MS_trans 'Tim'
SELECT * FROM tb_user A WHERE A.name = 'Lily'
SELECT * FROM tb_user A WHERE A.name = 'Tim'
以上是关于数据库工程开发秘籍之TSQL 存储过程user stored procedure的概念与案例实战的主要内容,如果未能解决你的问题,请参考以下文章