存储 频道

精讲:存储过程教程

  【IT168 技术】存储过程教程之存储过程的概念

  存储过程(Stored Procedure)是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库。中用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

  在SQL Server 的系列版本中存储过程分为两类:系统提供的存储过程和用户自定义存储过程。系统过程主要存储在master 数据库中并以sp_为前缀,并且系统存储过程主要是从系统表中获取信息,从而为系统管理员管理SQL Server 提供支持。通过系统存储过程,MS SQL Server 中的许多管理性或信息性的活动(如了解数据库对象、数据库信息)都可以被顺利有效地完成。尽管这些系统存储过程被放在master 数据库中,但是仍可以在其它数据库中对其进行调用,在调用时不必在存储过程名前加上数据库名。而且当创建一个新数据库时,一些系统存储过程会在新数据库中被自动创建。用户自定义存储过程是由用户创建并能完成某一特定功能(如查询用户所需数据信息)的存储过程。在本章中所涉及到的存储过程主要是指用户自定义存储过程。

  存储过程教程之存储过程的优点

  当利用MS SQL Server 创建一个应用程序时,Transaction-SQL 是一种主要的编程语言。若运用Transaction-SQL 来进行编程,有两种方法。其一是,在本地存储Transaction- SQL 程序,并创建应用程序向SQL Server 发送命令来对结果进行处理。其二是,可以把部分用Transaction-SQL 编写的程序作为存储过程存储在SQL Server 中,并创建应用程序来调用存储过程,对数据结果进行处理存储过程能够通过接收参数向调用者返回结果集,结果集的格式由调用者确定;返回状态值给调用者,指明调用是成功或是失败;包括针对数据库的操作语句,并且可以在一个存储过程中调用另一存储过程。

  我们通常更偏爱于使用第二种方法,即在使用SQL Server 存储过程中而不是在客户计算机上调用Transaction-SQL 编写的一段程序,原因在于存储过程具有以下优点:

  (1)存储过程允许标准组件式编程

  存储过程在被创建以后可以在程序中被多次调用,而不必重新编写该存储过程的SQL 语句。而且数据库专业人员可随时对存储过程进行修改,但对应用程序源代码毫无影响(因为应用程序源代码只包含存储过程的调用语句),从而极大地提高了程序的可移植性。

  (2)存储过程能够实现较快的执行速度

  如果某一操作包含大量的Transaction-SQL 代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的,在首次运行一个存储过程时,查询优化器对其进行分析、优化,并给出最终被存在系统表中的执行计划。而批处理的Transaction- SQL 语句在每次运行时都要进行编译和优化,因此速度相对要慢一些。

  (3)存储过程能够减少网络流量

  对于同一个针对数据数据库对象的操作(如查询、修改),如果这一操作所涉及到的 Transaction-SQL 语句被组织成一存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,否则将是多条SQL 语句,从而大大增加了网络流量,降低网络负载。

  (4)存储过程可被作为一种安全机制来充分利用

  系统管理员通过对执行某一存储过程的权限进行限制,从而能够实现对相应的数据访问权限的限制,避免非授权用户对数据的访问,保证数据的安全。(我们将在14 章“SQLServer 的用户和安全性管理”中对存储过程的这一应用作更为清晰的介绍)

  注意:存储过程虽然既有参数又有返回值,但是它与函数不同。存储过程的返回值只是指明执行是否成功,并且它不能像函数那样被直接调用,也就是在调用存储过程时,在存储过程名字前一定要有EXEC保留字。

  存储过程教程之存储过程的创建

  创建存储过程,存储过程是保存起来的可以接受和返回用户提供的参数的 Transact-SQL 语句的集合。

  可以创建一个过程供永久使用,或在一个会话中临时使用(局部临时过程),或在所有会话中临时使用(全局临时过程)。

  也可以创建在 Microsoft? SQL Server? 启动时自动运行的存储过程。

  语法

  CREATE PROC [ EDURE ] procedure_name [ ; number ]

  [ { @parameter data_type }

  [ VARYING ] [ = default ] [ OUTPUT ]

  ] [ ,...n ]

  [ WITH

  { RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ]

  [ FOR REPLICATION ]

  AS sql_statement [ ...n ]

  参数

  procedure_name

  新存储过程的名称。过程名必须符合标识符规则,且对于数据库及其所有者必须唯一。要创建局部临时过程,可以在 procedure_name 前面加一个编号符 (#procedure_name),要创建全局临时过程,可以在 procedure_name 前面加两个编号符 (##procedure_name)。完整的名称(包括 # 或 ##)不能超过 128 个字符。指定过程所有者的名称是可选的。

  ;number

  是可选的整数,用来对同名的过程分组,以便用一条 DROP PROCEDURE 语句即可将同组的过程一起除去。例如,名为 orders 的应用程序使用的过程可以命名为 orderproc;1、orderproc;2 等。DROP PROCEDURE orderproc 语句将除去整个组。如果名称中包含定界标识符,则数字不应包含在标识符中,只应在 procedure_name 前后使用适当的定界符。

  @parameter

  过程中的参数。在 CREATE PROCEDURE 语句中可以声明一个或多个参数。用户必须在执行过程时提供每个所声明参数的值(除非定义了该参数的默认值)。存储过程最多可以有 2.100 个参数。

  使用 @ 符号作为第一个字符来指定参数名称。参数名称必须符合标识符的规则。每个过程的参数仅用于该过程本身;相同的参数名称可以用在其它过程中。默认情况下,参数只能代替常量,而不能用于代替表名、列名或其它数据库对象的名称。

  data_type

  参数的数据类型。所有数据类型(包括 text、ntext 和 image)均可以用作存储过程的参数。不过,cursor 数据类型只能用于 OUTPUT 参数。如果指定的数据类型为 cursor,也必须同时指定 VARYING 和 OUTPUT 关键字。

  说明 对于可以是 cursor 数据类型的输出参数,没有最大数目的限制。

  VARYING

  指定作为输出参数支持的结果集(由存储过程动态构造,内容可以变化)。仅适用于游标参数。

  default

  参数的默认值。如果定义了默认值,不必指定该参数的值即可执行过程。默认值必须是常量或 NULL。如果过程将对该参数使用 LIKE 关键字,那么默认值中可以包含通配符(%、_、[] 和 [^])。

  OUTPUT

  表明参数是返回参数。该选项的值可以返回给 EXEC[UTE]。使用 OUTPUT 参数可将信息返回给调用过程。Text、ntext 和 image 参数可用作 OUTPUT 参数。使用 OUTPUT 关键字的输出参数可以是游标占位符。

  n

  表示最多可以指定 2.100 个参数的占位符。

  {RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION}

  RECOMPILE 表明 SQL Server 不会缓存该过程的计划,该过程将在运行时重新编译。在使用非典型值或临时值而不希望覆盖缓存在内存中的执行计划时,请使用 RECOMPILE 选项。

  ENCRYPTION 表示 SQL Server 加密 syscomments 表中包含 CREATE PROCEDURE 语句文本的条目。使用 ENCRYPTION 可防止将过程作为 SQL Server 复制的一部分发布。

  说明 在升级过程中,SQL Server 利用存储在 syscomments 中的加密注释来重新创建加密过程。

  FOR REPLICATION

  指定不能在订阅服务器上执行为复制创建的存储过程。.使用 FOR REPLICATION 选项创建的存储过程可用作存储过程筛选,且只能在复制过程中执行。本选项不能和 WITH RECOMPILE 选项一起使用。

  AS

  指定过程要执行的操作。

  sql_statement

  过程中要包含的任意数目和类型的 Transact-SQL 语句。但有一些限制。

  n

  是表示此过程可以包含多条 Transact-SQL 语句的占位符。

  注释

  存储过程的最大大小为 128 MB。

  用户定义的存储过程只能在当前数据库中创建(临时过程除外,临时过程总是在 tempdb 中创建)。在单个批处理中,CREATE PROCEDURE 语句不能与其它 Transact-SQL 语句组合使用。

  默认情况下,参数可为空。如果传递 NULL 参数值并且该参数在 CREATE 或 ALTER TABLE 语句中使用,而该语句中引用的列又不允许使用 NULL,则 SQL Server 会产生一条错误信息。为了防止向不允许使用 NULL 的列传递 NULL 参数值,应向过程中添加编程逻辑或为该列使用默认值(使用 CREATE 或 ALTER TABLE 的 DEFAULT 关键字)。

  建议在存储过程的任何 CREATE TABLE 或 ALTER TABLE 语句中都为每列显式指定 NULL 或 NOT NULL,例如在创建临时表时。ANSI_DFLT_ON 和 ANSI_DFLT_OFF 选项控制 SQL Server 为列指派 NULL 或 NOT NULL 特性的方式(如果在 CREATE TABLE 或 ALTER TABLE 语句中没有指定的话)。如果某个连接执行的存储过程对这些选项的设置与创建该过程的连接的设置不同,则为第二个连接创建的表列可能会有不同的为空性,并且表现出不同的行为方式。如果为每个列显式声明了 NULL 或 NOT NULL,那么将对所有执行该存储过程的连接使用相同的为空性创建临时表。

  在创建或更改存储过程时,SQL Server 将保存 SET QUOTED_IDENTIFIER 和 SET ANSI_NULLS 的设置。执行存储过程时,将使用这些原始设置。因此,所有客户端会话的 SET QUOTED_IDENTIFIER 和 SET ANSI_NULLS 设置在执行存储过程时都将被忽略。在存储过程中出现的 SET QUOTED_IDENTIFIER 和 SET ANSI_NULLS 语句不影响存储过程的功能。

  其它 SET 选项(例如 SET ARITHABORT、SET ANSI_WARNINGS 或 SET ANSI_PADDINGS)在创建或更改存储过程时不保存。如果存储过程的逻辑取决于特定的设置,应在过程开头添加一条 SET 语句,以确保设置正确。从存储过程中执行 SET 语句时,该设置只在存储过程完成之前有效。之后,设置将恢复为调用存储过程时的值。这使个别的客户端可以设置所需的选项,而不会影响存储过程的逻辑。

  存储过程教程之示例

  A. 使用带有复杂 SELECT 语句的简单过程

  下面的存储过程从四个表的联接中返回所有作者(提供了姓名)、出版的书籍以及出版社。该存储过程不使用任何参数。

  USE pubs

  IF EXISTS (SELECT name FROM sysobjects

  WHERE name = \'au_info_all\' AND type = \'P\')

  DROP PROCEDURE au_info_all

  GO

  CREATE PROCEDURE au_info_all

  AS

  SELECT au_lname, au_fname, title, pub_name

  FROM authors a INNER JOIN titleauthor ta

  ON a.au_id = ta.au_id INNER JOIN titles t

  ON t.title_id = ta.title_id INNER JOIN publishers p

  ON t.pub_id = p.pub_id

  GO

  au_info_all 存储过程可以通过以下方法执行:

  EXECUTE au_info_all

  -- Or

  EXEC au_info_all

  如果该过程是批处理中的第一条语句,则可使用:

  au_info_all

  -------------------------

  B. 使用带有参数的简单过程

  下面的存储过程从四个表的联接中只返回指定的作者(提供了姓名)、出版的书籍以及出版社。该存储过程接受与传递的参数精确匹配的值。

  USE pubs

  IF EXISTS (SELECT name FROM sysobjects

  WHERE name = \'au_info\' AND type = \'P\')

  DROP PROCEDURE au_info

  GO

  USE pubs

  GO

  CREATE PROCEDURE au_info

  @lastname varchar(40),

  @firstname varchar(20)

  AS

  SELECT au_lname, au_fname, title, pub_name

  FROM authors a INNER JOIN titleauthor ta

  ON a.au_id = ta.au_id INNER JOIN titles t

  ON t.title_id = ta.title_id INNER JOIN publishers p

  ON t.pub_id = p.pub_id

  WHERE au_fname = @firstname

  AND au_lname = @lastname

  GO

  au_info 存储过程可以通过以下方法执行:

  EXECUTE au_info \'Dull\', \'Ann\'

  -- Or

  EXECUTE au_info @lastname = \'Dull\', @firstname = \'Ann\'

  -- Or

  EXECUTE au_info @firstname = \'Ann\', @lastname = \'Dull\'

  -- Or

  EXEC au_info \'Dull\', \'Ann\'

  -- Or

  EXEC au_info @lastname = \'Dull\', @firstname = \'Ann\'

  -- Or

  EXEC au_info @firstname = \'Ann\', @lastname = \'Dull\'

  如果该过程是批处理中的第一条语句,则可使用:

  au_info \'Dull\', \'Ann\'

  -- Or

  au_info @lastname = \'Dull\', @firstname = \'Ann\'

  -- Or

  au_info @firstname = \'Ann\', @lastname = \'Dull\'

  -------------------------

  C. 使用带有通配符参数的简单过程

  下面的存储过程从四个表的联接中只返回指定的作者(提供了姓名)、出版的书籍以及出版社。该存储过程对传递的参数进行模式匹配,如果没有提供参数,则使用预设的默认值。

  USE pubs

  IF EXISTS (SELECT name FROM sysobjects

  WHERE name = \'au_info2\' AND type = \'P\')

  DROP PROCEDURE au_info2

  GO

  USE pubs

  GO

  CREATE PROCEDURE au_info2

  @lastname varchar(30) = \'D%\',

  @firstname varchar(18) = \'%\'

  AS

  SELECT au_lname, au_fname, title, pub_name

  FROM authors a INNER JOIN titleauthor ta

  ON a.au_id = ta.au_id INNER JOIN titles t

  ON t.title_id = ta.title_id INNER JOIN publishers p

  ON t.pub_id = p.pub_id

  WHERE au_fname LIKE @firstname

  AND au_lname LIKE @lastname

  GO

  au_info2 存储过程可以用多种组合执行。下面只列出了部分组合:

  EXECUTE au_info2

  -- Or

  EXECUTE au_info2 \'Wh%\'

  -- Or

  EXECUTE au_info2 @firstname = \'A%\'

  -- Or

  EXECUTE au_info2 \'[CK]ars[OE]n\'

  -- Or

  EXECUTE au_info2 \'Hunter\', \'Sheryl\'

  -- Or

  EXECUTE au_info2 \'H%\', \'S%\'

  -------------------------

  D. 使用 OUTPUT 参数

  OUTPUT 参数允许外部过程、批处理或多条 Transact-SQL 语句访问在过程执行期间设置的某个值。下面的示例创建一个存储过程 (titles_sum),并使用一个可选的输入参数和一个输出参数。

  首先,创建过程:

  USE pubs

  GO

  IF EXISTS(SELECT name FROM sysobjects

  WHERE name = \'titles_sum\' AND type = \'P\')

  DROP PROCEDURE titles_sum

  GO

  USE pubs

  GO

  CREATE PROCEDURE titles_sum @@TITLE varchar(40) = \'%\', @@SUM money OUTPUT

  AS

  SELECT \'Title Name\' = title

  FROM titles

  WHERE title LIKE @@TITLE

  SELECT @@SUM = SUM(price)

  FROM titles

  WHERE title LIKE @@TITLE

  GO

  接下来,将该 OUTPUT 参数用于控制流语言。

  说明 OUTPUT 变量必须在创建表和使用该变量时都进行定义。

  参数名和变量名不一定要匹配,不过数据类型和参数位置必须匹配(除非使用 @@SUM = variable 形式)。

0
相关文章