SQL 使用存储过程

存储过程
       我们使用的大多数SQL语句都是针对一个或多个表的单条语句。并非所有的操作都这么简单,经常会有一些复杂的操作需要多条语句才能完成。例如以下的情形:
    1. 为了处理订单,必须核对以保证库存中有相应的物品。
    2. 如果物品有库存,需要预定,不再出售给别的人,并且减少物品数据以反映正确的库存量。
    3. 库存中没有的物品需要订购,这需要与供应商进行某种交互。
    4. 关于哪些物品入库(并且可以立即发货)和哪些物品退订,需要通知相应的顾客。
       执行这个处理需要针对许多表的多条SQL语句。此外,需要执行的具体SQL语句及其次序也不是固定的,它们可能会根据物品是否在库存中而变化。 那么,怎样编写代码呢?可以单独编写每条SQL语句,并根据结果有条件地执行其他语句。在每次需要这个处理时(以及每个需要它的应用中),都必须做这些工作。
       可以创建存储过程。简单来说,存储过程就是为以后使用而保存的一条或多条SQL语句。可将其视为批文件,虽然它们的作用不仅限于批处理。

下面是使用存储过程的优点:
    1、通过把处理封装在一个易用的单元中,可以简化复杂的操作(如前面例子所述)。
    2、由于不要求反复建立一系列处理步骤,因而保证了数据的一致性。如果所有开发人员和应用程序都使用同一存储过程,则所使用的代码都是相同的。
这一点的延伸就是防止错误。需要执行的步骤越多,出错的可能性就越大。防止错误保证了数据的一致性。
    3、简化对变动的管理。如果表名、列名或业务逻辑(或别的内容)有变化,那么只需要更改存储过程的代码。使用它的人员甚至不需要知道这些变化。
这一点的延伸就是安全性。通过存储过程限制对基础数据的访问,减少了数据讹误(无意识的或别的原因所导致的数据讹误)的机会。
    4、因为存储过程通常以编译过的形式存储,所以DBMS处理命令的工作较少,提高了性能。
    5、存在一些只能用在单个请求中的SQL元素和特性,存储过程可以使用它们来编写功能更强更灵活的代码。
       换句话说,使用存储过程有三个主要的好处,即简单、安全、高性能。显然,它们都很重要。不过,在将SQL代码转换为存储过程前,也必须知道它的一些缺陷。
    1、不同DBMS中的存储过程语法有所不同。事实上,编写真正的可移植存储过程几乎是不可能的。不过,存储过程的自我调用(名字以及数据如何传递)可以相对保持可移植。因此,如果需要移植到别的DBMS,至少客户端应用代码不需要变动。
    2、一般来说,编写存储过程比编写基本SQL语句复杂,需要更高的技能,更丰富的经验。因此,许多数据库管理员把限制存储过程的创建作为安全措施(主要受上一条缺陷的影响)。

执行存储过程
       存储过程的执行远比编写要频繁得多,因此我们先介绍存储过程的执行。执行存储过程的SQL语句很简单,即EXECUTE。EXECUTE接受存储过程名和需要传递给它的任何参数。请看下面的例子:
输入▼
EXECUTE  AddNewProduct( 'JTS01',
'Stuffed Eiffel Tower',
6.49,
'Plush stuffed toy with the text La
➥Tour Eiffel in red white and blue' );
分析▼
       这里执行一个名为A ddNewProduct的存储过程,将一个新产品添加到Products表中。A ddNewProduct有四个参数,分别是:供应商ID(Vendors表的主键)、产品名、价格和描述。这4个 参数匹配存储过程中4个预期变量(定义为存储过程自身的组成部分)。此存储过程将新行添加到Products表,并将传入的属性赋给相应的列。
       我们注意到,在Products表中还有另一个需要值的列prod_id列,它是这个表的主键。为什么这个值不作为属性传递给存储过程?要保证恰当地生成此ID,最好是使生成此ID的过程自动化(而不是依赖于最终用户的输入)。这也是这个例子使用存储过程的原因。以下是存储过程所完成的工作:
    1、验证传递的数据,保证所有4个参数都有值;
    2、生成用作主键的唯一ID;
    3、将新产品插入Products表,在合适的列中存储生成的主键和传递的数据。
这就是存储过程执行的基本形式。对于具体的DBMS,可能包括以下的执行选择:
    1、参数可选,具有不提供参数时的默认值;
    2、不按次序给出参数,以“参数=值”的方式给出参数值。
    3、输出参数,允许存储过程在正执行的应用程序中更新所用的参数。
    4、用SELECT语句检索数据。
    5、返回代码,允许存储过程返回一个值到正在执行的应用程序。

创建存储过程
一个简单的存储过程例子,它对邮件发送清单中具有邮件地址的顾客进行计数。
下面是该过程的Oracle版本:
输入▼
CREATE  PROCEDURE  MailingListCount (
ListCount OUT INTEGER
)
IS
v_rows INTEGER;
BEGIN
SELECT COUNT(*) INTO v_rows
FROM Customers
WHERE NOT cust_email IS NULL;
ListCount := v_rows;
END;
分析▼
       这个存储过程有一个名为ListCount的参数。此参数从存储过程返回一个值而不是传递一个值给存储过程。关键字OUT用来指示这种行为。
       Oracle支持IN(传递值给存储过程)、OUT(从存储过程返回值,如这里)、INOUT(既传递值给存储过程也从存储过程传回值)类型的参数。存储过程的代码括在BEGIN和END语句中,这里执行一条简单的SELECT语句,它检索具有邮件地址的顾客。然后用检索出的行数设置ListCount(要传递的输出参数)。
调用Oracle例子可以像下面这样:
输入▼
var ReturnValue NUMBER
EXEC MailingListCount(:ReturnValue);
SELECT ReturnValue;
分析▼
       这段代码声明了一个变量来保存存储过程返回的任何值,然后执行存储过程,再使用SELECT语句显示返回的值。
下面是该过程的SQL Server版本:
输入▼
CREATE PROCEDURE MailingListCount
AS
DECLARE  @cnt INTEGER
SELECT  @cnt = COUNT(*)
FROM  Customers
WHERE  NOT  cust_email  IS  NULL;
RETURN  @cnt;
分析▼
       此存储过程没有参数。调用程序检索SQL Server的返回代码支持的值。其中用DECLA RE语句声明了一个名为@cnt的局部变量(SQL Server中所有局部变量名都以@起头);然后在SELECT语句中使用这个变量,让它包含COUNT()函数返回的值;最后,用RETURN @cnt语句将计数返回给调用程序。
调用SQL Server例子可以像下面这样:
输入▼
DECLARE  @ReturnValue INT
EXECUTE  @ReturnValue=MailingListCount;
SELECT 	 @ReturnValue;
分析▼
这段代码声明了一个变量来保存存储过程返回的任何值,然后执行存储过程,再使用SELECT语句显示返回的值。
下面是另一个例子,这次在Orders表中插入一个新订单。此程序仅适用于SQL Server,但它说明了存储过程的某些用途和技术:
输入▼
CREATE PROCEDURE NewOrder @cust_id CHAR(10)
AS
-- Declare variable for order number
DECLARE @order_num INTEGER
-- Get current highest order number
SELECT @order_num=MAX(order_num)
FROM Orders
-- Determine next order number
SELECT @order_num=@order_num+1
-- Insert new order
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(@order_num, GETDATE(), @cust_id)
-- Return order number
RETURN @order_num;
分析▼
       此存储过程在Orders表中创建一个新订单。它只有一个参数,即下订单顾客的ID。订单号和订单日期这两列在存储过程中自动生成。代码首先声明一个局部变量来存储订单号。接着,检索当前最大订单号(使用MA X()函数)并增加1(使用SELECT语句)。然后用INSERT语句插入由新生成的订单号、当前系统日期(用GETDA TE()函数检索)和传递的顾客ID组成的订单。最后,用 RETURN @order_num返回订单号(处理订单物品需要它)。请注意,此代码加了注释,在编写存储过程时应该多加注释。
说明:注释代码
       应该注释所有代码,存储过程也不例外。增加注释不影响性能,因此不存在缺陷(除了增加编写时间外)。注释代码的好处很多,包括使别人(以及你自己)更容易地理解和更安全地修改代码。
       对代码进行注释的标准方式是在之前放置--(两个连字符)。有的DBMS还支持其他的注释语法,不过所有DBMS都支持--,因此在注释代码时最好都使用这种语法。
下面是相同SQL Server代码的一个很不同的版本:
输入▼
CREATE PROCEDURE NewOrder @cust_id CHAR(10)
AS
-- Insert new order
INSERT INTO Orders(cust_id)
VALUES(@cust_id)
-- Return order number
SELECT order_num = @@IDENTITY;
分析▼
       此存储过程也在Orders表中创建一个新订单。这次由DBMS生成订单号。大多数DBMS都支持这种功能;SQL Server中称这些自动增量的列为标识字段(identity field),而其他DBMS称之为自动编号(auto number)或序列(sequence)。传递给此过程的参数也是一个,即下订单的顾客ID。订单号和订单日期没有给出,DBMS对日期使用默认值(GETDA TE()函数),订单号自动生成。怎样才能得到这个自动生成的ID?在SQL Server上可在全局变量@@IDENTITY中得到,它返回到调用程序(这里使用SELECT语句)。
       可以看到,借助存储过程,可以有多种方法完成相同的工作。不过,所选择的方法受所用DBMS特性的制约。

猜你喜欢

转载自blog.csdn.net/gui1259802368/article/details/80738715