39 存储过程
39.1 存储过程的定义
存储过程其实就是已预编译为可执行过程的一个或多个SQL语句。通过调用和传递参数即可完成该存储过程的功能。
39.2 创建存储过程语法
CREATE PROC|PROCEDURE procedure_name [{@参数数据类型} [=默认值] [OUTPUT], {@参数数据类型} [=默认值] [OUTPUT], ...... ] AS sql_statements GO
简单示例:
CREATE PROCEDURE sp_test @param1 INT, @param2 VARCHAR(16) AS SELECT * FROM test WHERE id=@param1 AND t_no=@param2; GO
注意:存储过程在创建阶段可以带参数或不带参数,不带参数的一般是执行一些不需要传递参数的语句就可以完成的功能,带参数那就是需要传递参数的SQL语句,就像上面的示例那样,传递了两个参数给SQL语句。带参数的一定要定义参数类型,是字符型的还要定义长度,给参数加默认值是可选的。
39.3 存储过程的优点
(1)提高性能
SQL语句在创建过程时进行分析和编译。存储过程是预编译的,在首次运行一个存储过程时,查询优化器对其进行分析、优化,并给出最终被存在系统表中的存储计划,这样,在执行过程时便可节省此开销。
(2)降低网络开销
存储过程调用时,只需用提供存储过程名和必要的参数信息,从而可降低网络的流量。
(3)便于进行代码移植
数据库专业人员可以随时对存储过程进行修改,但对应用程序源代码却毫无影响,从而极大的提高了程序的可移植性。
(4)更强的安全性
a. 系统管理员可以对执行的某一个存储过程进行权限限制,避免非授权用户对数据的访问。
b. 在通过网络调用过程时,只有对执行过程的调用是可见的。因此,恶意用户无法看到表和数据库对象名称、嵌入自己的Transact-SQL语句或搜索关键数据。
c. 使用过程参数有助于避免SQL注入攻击。因为参数输入被视作文字值而非可执行代码,所以,攻击者将命令插入过程内的Transact-SQL语句并损害安全性更为困难。
d. 可以对过程进行加密,这有助于对源代码进行模糊处理。
39.4 存储过程的缺点
(1)逻辑处理吃力
SQL本身是一种结构化查询语言,但不是面向对象的,本质上还是过程化的语言,面对复杂的业务逻辑,过程化的处理会很吃力。同时SQL擅长的是数据查询而非业务逻辑的处理,如果把业务逻辑全放在存储过程里面,违背了这一原则。
(2)修改参数复杂
如果需要对输入存储过程的参数进行修改,或者要更改由其返回的数据,则仍需要更新程序集中的代码以及添加参数、更新调用,等等,这时候估计会比较繁琐了。
(3)开发调试复杂
由于IDE(Integrated Development Environment,集成开发环境)的问题,存储过程的开发调试要比一般程序困难。
(4)无法应用缓存
虽然有全局临时表之类的方法可以做缓存,但同样加重了数据库的负担。如果缓存并发严重,经常要加锁,那效率实在堪忧。
(5)不支持群集
数据库服务器无法水平扩展,或者数据库的切割(水平或垂直切割)。数据库切割之后,存储过程并不清楚数据存储在哪个数据库中。
39.5 创建不带参数的存储过程
示例:查询订单表中订单总数
--查询存储过程 IF OBJECT_ID (N'PROC_ORDER_COUNT',N'P') IS NOT NULL DROP PROCEDURE PROC_ORDER_COUNT; GO CREATE PROCEDURE PROC_ORDER_COUNT AS SELECT COUNT(OrderID) FROM Orders; GO
执行上述存储过程:
EXEC PROC_ORDER_COUNT;
39.6 创建带参数的存储过程
示例:根据城市查询订单数量
--查询存储过程,根据城市查询订单总数 IF OBJECT_ID(N'PROC_ORDER_COUNT',N'P') IS NOT NULL DROP PROCEDURE PROC_ORDER_COUNT; GO CREATE PROCEDURE PROC_ORDER_COUNT(@city NVARCHAR(50)) AS SELECT COUNT(OrderID) FROM Orders WHERE City=@city GO
执行上述存储过程:
EXEC PROC_ORDER_COUNT N'广州';
39.7 参数带通配符
--查询订单编号头两位是LJ的订单信息,含通配符 IF OBJECT_ID(N'PROC_ORDER_INFO',N'P') IS NOT NULL DROP PROCEDURE PROC_ORDER_INFO; GO CREATE PROCEDURE PROC_ORDER_INFO @OrderID NVARCHAR(50)='LJ%';--默认值 AS SELECT OrderID,City,OrderDate,Price FROM Orders WHERE OrderID LIKE @OrderID; GO
执行上述存储过程:
EXEC PROC_ORDER_INFO; EXEC PROC_ORDER_INFO N'LJ%'; EXEC PROC_ORDER_INFO N'%LJ%';
39.8 带输出参数
--根据订单查询的信息,返回订单的城市及单价 IF OBJECT_ID(N'PROC_ORDER_INFO',N'P') IS NOT NULL DROP PROCEDURE PROC_ORDER_INFO; GO CREATE PROCEDURE PROC_ORDER INFO @orderid NVARCHAR(50), --输入参数 @city NVARCHAR(20) OUT --输出参数 @price FLOAT OUTPUT --输入输出参数 AS SELECT @city=City,@price=Price FROM Orders WHERE OrderID=@orderid AND Price=@price; GO
执行上述存储过程:
DECLARE @orderid NVARCHAR(50), @city NVARCHAR(20), @price INT; SET @orderid=N'LJ0001'; SET @price=35.21; EXEC PROC_ORDER_INFO @orderid,@city OUT,@price OUTPUT; SELECT @city,@price;
39.9 存储过程中的插入
--新增订单信息 IF OBJECT_ID (N'PROC_INSERT_ORDER',N'P') IS NOT NULL DROP PROCEDURE PROC_INSERT_ORDER; GO CREATE PROCEDURE PROC_INSERT_ORDER @orderid NVARCHAR(50), @city NVARCHAR(20), @price FLOAT AS INSERT INTO Orders(OrderID,City,Price) VALUES(@orderid,@city,@price) GO
执行上述存储过程:
EXEC PROC_INSERT_ORDER N'LJ0001',N'广州',35.21;
39.10 存储过程中的更新
--修改订单信息 IF OBJECT_ID(N'PROC_UPDATE_ORDER',N'P') IS NOT NULL DROP PROCEDURE PROC_UPDATE_ORDER; GO CREATE PROCEDURE PROC_UPDATE_ORDER @orderid NVARCHAR(50), @city NVARCHAR(20), @price FLOAT AS UPDATE Orders SET OrderID=@orderid,City=@city,Price=@price; GO
执行上述存储过程:
EXEC PROC_UPDATE_ORDER N'LJ0001',N'上海',37.21;
39.11 存储过程中的删除
--删除订单信息 IF OBJECT_ID (N'PROC_DELETE_ORDER',N'P') IS NOT NULL DROP PROCEDURE PROC_DELETE_ORDER; GO CREATE PROCEDURE PROC_DELETE_ORDER @orderid NVARCHAR(50) AS DELETE FROM Orders WHERE OrderID=@orderid; GO
执行上述存储过程:
EXEC PROC_DELETE_ORDER N'LJ0001';
39.12 重复编译存储过程
重复编译存储过程的目的是为了提供存储过程的执行效率。
--重复编译 IF OBJECT_ID (N'PROC_ORDER_WITH_RECOMPILE',N'P') IS NOT NULL DROP PROCEDURE PROC_ORDER_WITH_RECOMPILE; GO CREATE PROCEDURE PROC_ORDER_WITH_RECOMPILE WITH RECOMPILE --重复编译 AS SELECT * FROM Orders; GO
39.13 加密存储过程
--查询存储过程,进行加密。加密后不能查看和修改源脚本 IF OBIECT_ID (N'PROC_ORDER_WITH_ENCRYPTION',N'P') IS NOT NULL DROP PROCEDURE PROC_ORDER_WITH_ENCRYPTION; GO CREATE PROCEDURE PROC_ORDER_WITH_ENCRYPTION WITH ENCRYPTION --加密 AS SELECT * FROM Orders; GO
执行上述存储过程:
EXEC PROC_ORDER_WITH_ENCRYPTION;
39.14 批注
存储过程在业务量小的系统中应用比较广泛,将逻辑全部交给数据库进行处理,可以快速完成一些逻辑功能。但是针对业务量比较大的系统,一般是禁止使用存储过程的,因为对数据库的压力太大。