第六章SQL数据库开发--TSQL—储存过程
存储过程 (Stored Procedure) 是在大型数据库系统中 , 一组为了完成特定功能的 SQL 语句集 , 存储在数据库中 , 经过第一次编译后再次调用不需要再次编译 , 用户通过指定存储过程的名字并给出参数 (如果该存储过程带有参数) 来执行它 , 存储过程是数据库中的一个重要对象 ;
1 接受输入参数并以输出参数的格式向调用程序返回多个值。
2 包含用于在数据库中执行操作的编程语句。 这包括调用其他过程。
3 向调用程序返回状态值,以指明成功或失败(以及失败的原因)。
存储过程加快系统运行速度,存储过程只在创建时编译,以后每次执行时不需要重新编译。
存储过程可以封装复杂的数据库操作,简化操作流程,例如对多个表的更新,删除等。
可实现模块化的程序设计,存储过程可以多次调用,提供统一的数据库访问接口,改进应用程序的可维护性。
存储过程可以增加代码的安全性,对于用户不能直接操作存储过程中引用的对象,SQL Server可以设定用户对指定存储过程的执行权限。
存储过程可以降低网络流量,存储过程代码直接存储于数据库中,在客户端与服务器的通信过程中,不会产生大量的T_SQL代码流量。
存储过程的缺点:
数据库移植不方便,存储过程依赖与数据库管理系统, SQL Server 存储过程中封装的操作代码不能直接移植到其他的数据库管理系统中。
不支持面向对象的设计,无法采用面向对象的方式将逻辑业务进行封装,甚至形成通用的可支持服务的业务逻辑框架.
代码可读性差,不易维护。不支持集群。
用户定义
用户定义的过程可在用户定义的数据库中创建,或者在除了 Resource 数据库之外的所有系统数据库中创建。 该过程可在 Transact-SQL 中开发,或者作为对 Microsoft .NET Framework 公共语言运行时 (CLR) 方法的引用开发。
临时
临时过程是用户定义过程的一种形式。 临时过程与永久过程相似,只是临时过程存储于 tempdb中。 临时过程有两种类型:本地过程和全局过程。 它们在名称、可见性以及可用性上有区别。 本地临时过程的名称以单个数字符号 (#) 开头;它们仅对当前的用户连接是可见的;当用户关闭连接时被删除。 全局临时过程的名称以两个数字符号 (##) 开头,创建后对任何用户都是可见的,并且在使用该过程的最后一个会话结束时被删除。
系统
系统过程是 SQL Server随附的。 它们物理上存储在内部隐藏的 Resource 数据库中,但逻辑上出现在每个系统定义数据库和用户定义数据库的 sys 架构中。 此外, msdb 数据库还在 dbo 架构中包含用于计划警报和作业的系统存储过程。 因为系统过程以前缀 sp_ 开头,所以,我们建议你在命名用户定义过程时不要使用此前缀。
CREATE [ OR ALTER ] { PROC | PROCEDURE }
[schema_name.] procedure_name [ ; number ]
[ { @parameter [ type_schema_name. ] data_type }
[ VARYING ] [ = default ] [ OUT | OUTPUT | [READONLY]
] [ ,...n ]
[ WITH
[ FOR REPLICATION ]
AS { [ BEGIN ] sql_statement [;] [ ...n ] [ END ] }
[;]
[ ENCRYPTION ]
[ RECOMPILE ]
[ EXECUTE AS Clause ]
简写语法
CREATE [ OR ALTER ] { PROC | PROCEDURE }
[schema_name.] procedure_name [ ; number ]
[ { @parameter [ type_schema_name. ] data_type }
[ VARYING ] [ = default ] [ OUT | OUTPUT | [READONLY]
] [ ,...n ]
AS { [ BEGIN ] sql_statement [;] [ ...n ] [ END ] }
CREATE PROC 关键字
procedure_name 存储过程名称
number 对存储过程进行分组
@parameter 存错过程参数
data_type 数据类型
VARYING 指定作为输出参数支持的结果集,改参数仅适用于游标参数。
Default 可选项,参数默认值
OUTPUT 将参数返回值给调用的过程
AS 制定存储过程的操作
SQL_STATEMENT 存储过程的过程体。
举例
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName>
-- Add the parameters for the stored procedure here
<@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>,
<@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT <@Param1, sysname, @p1>, <@Param2, sysname, @p2>
END
GO
当ANSI_NULLS 为ON时,遵循SQL92的标准,只能使用IS NULL 来判断值是否为NULL, 而不能使用=或<>来与NULL做比较,任何值包括NULL值与NULL值做=或<>运算都得到FALSE。
当ANSI_NULLS为OFF时,将不再遵循SQL92标准,可以使用=和<>来与NULL做BOOL运算。
当SET QUOTED_IDENTIFIER值为ON时,双引号内的字符被当作是数据库对象。就是说双引号" "和标识符[]效果是一样样的,他们都表示引用的字符是数据库对象。单引号'表示字符串的边界。
当SET QUOTDE_IDENTIFIER OFF时,双引号被解释为字符串的边界,和单引号的作用是类似的。就是说双引号"不能当做标识符使用,但是可以当做字符边界,和单引号'的效果是一样样的。
可以做一个总结:当SET QUOTED_IDENTIFIER ON " "等同于[ ] 表示数据库对象;当SET QUOTED_IDENTIFIER OFF " "等同于' '表示字符串边界;还有这里的双引号" 并不是两个单引号'合起来的,是shift+”打出来的,初学者可能会犯这样的错误。
SET NOCOUNT
使返回的结果中不包含有关受 Transact-SQL 语句影响的行数的信息。
语法
SET NOCOUNT { ON | OFF }
注释
当 SET NOCOUNT 为 ON 时,不返回计数(表示受 Transact-SQL 语句影响的行数)。当 SET NOCOUNT 为 OFF 时,返回计数。
即使当 SET NOCOUNT 为 ON 时,也更新 @@ROWCOUNT 函数。
USE [T_BRANCH]
GO
/****** Object: StoredProcedure [dbo].[ZH_Proc_Insert_M02_VERSION_SYJ ] Script Date: 2018/12/5 21:38:53 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[ZH_Proc_Insert_M02_VERSION_SYJ ]
@PLAZA_ID int,
@LANE_ID int,
@TABLE_ID INT,
@VERSION VARCHAR(50),
@CURENT_TIME VARCHAR(50)
AS
BEGIN
BEGIN TRY
BEGIN
INSERT INTO [dbo].[M02_VERSION_SYJ]
([PLAZA_ID]
,[LANE_ID]
,[TABLE_ID]
,[VERSION]
,[CURENT_TIME])
VALUES
(@PLAZA_ID ,
@LANE_ID ,
@TABLE_ID, @VERSION,
@CURENT_TIME )
END
END TRY
BEGIN CATCH
BEGIN
IF @@Error = 2627 goto Repeat
else goto ErrMsg
END
END CATCH
SELECT 1 AS RESULT, '执行成功!' AS WARNING
RETURN 1;
Repeat:
BEGIN
UPDATE [dbo].[M02_VERSION_SYJ] SET VERSION = @VERSION ,[CURENT_TIME] = @CURENT_TIME
WHERE [PLAZA_ID] = @PLAZA_ID and [LANE_ID] = @LANE_ID and [TABLE_ID] = @TABLE_ID
IF @@Error <> 0 or @@rowcount = 0 goto ErrMsg
SELECT 2 AS RESULT, '进行更新!' AS WARNING
RETURN 2 --进行更新
END
ErrMsg:
BEGIN
SELECT @@Error
SELECT -1 AS RESULT, '更新失败!' AS WARNING
RETURN -1 --添加失败并回滚
END
END
GO
带参数输出的存储过程,主要是给调用程序返回值。
CREATE PROCEDURE HumanResources.uspEncryptThis
WITH ENCRYPTION
AS
SET NOCOUNT ON;
SELECT BusinessEntityID, JobTitle, NationalIDNumber,
VacationHours, SickLeaveHours
FROM HumanResources.Employee;
GO
HumanResources.Employee 表是加密的,只有使用WITH ENCRYPTION选项,才可以执行成功。
返回执行的上一个 Transact-SQL 语句的错误号,没有错误,执行成功,则返回 0
@@ERROR不等于0,就是存储过程执行失败。@@ERROR=2627 解释就是不能插入重复键。
BEGIN TRY
begin
INSERT INTO [L01_EVENTS]([MID],[PLAZA_ID],[LANE_ID],[OP_ID],[TIME_BEGIN],[OCCUR_TIME ],[SHIFT_ID],[EVENT_TYPE],[SEND_FLAG])
VALUES (@MID ,@PLAZA_ID ,@LANE_ID ,@OP_ID,@TIME_BEGIN,@OCCUR_TIME,@SHIFT_ID ,@EVENT_TYPE ,@SEND_FLAG)
END
END TRY
BEGIN CATCH
BEGIN
IF @@Error = 2627 goto Repeat
else goto ErrMsg
Transact-SQL 语句组可以包含在 TRY 块中。 如果 TRY 块内部发生错误,则会将控制传递给 CATCH 块中包含的另一个语句组。
语法
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
[ ; ]
使用条件
1 TRY…CATCH 构造可对严重程度高于 10 但不关闭数据库连接的所有执行错误进行缓存。
2 TRY 块后必须紧跟相关联的 CATCH 块。 在 END TRY 和 BEGIN CATCH 语句之间放置任何其他语句都将生成语法错误。
3 TRY…CATCH 构造不能跨越多个批处理。 TRY…CATCH 构造不能跨越多个 Transact-SQL 语句块。 例如,TRY…CATCH 构造不能跨越 Transact-SQL 语句的两个 BEGIN…END 块,且不能跨越 IF…ELSE 构造。
4 当11至19级别错误时,SQL才会立即对出TRY并转向CATCH块,
ERROR_NUMBER() 返回错误编号。
ERROR_SEVERITY() 返回严重性。
ERROR_STATE() 返回错误状态号。
ERROR_PROCEDURE() 返回出现错误的存储过程或触发器的名称。
ERROR_LINE() 返回导致错误的例程中的行号。
ERROR_MESSAGE()
错误级别 |
错误信息 |
描述 |
1-10 |
只是信息错误 |
这包括了上下文的更改,如调整了设置或者聚合运算时出现了NULL,这些不会触发CATCH块,因此如果需要测试这个错误级别,需要手动使用@@ERROR |
11-19 |
相对严重错误 |
这些大多数时由于代码处理的错误(例如主键和外键)。有些错误比较严重,你可能不希望继续处理(例如超出内存错误),但至少可以捕捉他们并适当退出 |
20-25 |
非常严重 |
这些错误通常为系统级错误,因此脚本和连接将立即终止 |
BEGIN TRY
BEGIN
INSERT INTO [dbo].[M02_VERSION_PARAM]
([PLAZA_ID]
,[LANE_ID]
,[TABLE_ID]
,[TABLE_NAME]
,[Effect_VERSION]
,[NotEffect_VERSION]
,[CURENT_TIME])
VALUES
(@PLAZA_ID ,
@LANE_ID ,
@TABLE_ID,
@TABLE_NAME ,
@Effect_VERSION,
@NotEffect_VERSION,
@CURENT_TIME )
END
END TRY
BEGIN CATCH
BEGIN
IF @@Error = 2627 goto Repeat ##插入重复行
else goto ErrMsg
END
END CATCH
简写
EXEC ZH_Proc_Insert_M02_VERSION_SYJ 100891, 4, 151101, 1812050000220717, '2018-12-05 10:31:17';
2 进行更新!
复杂写法带参数名称
EXEC ZH_Proc_Insert_M02_VERSION_SYJ @PLAZA_ID=100891, @LANE_ID=100, @TABLE_ID=151101, @VERSION=1812050000220717, @CURENT_TIME='2018-12-05 10:31:17';