|
SQL-99允许定义函数、过程和方法。定义可以通过SQL-99的有关过程的组件,也可以通过外部的程序设计语言,例如Java、C或C++。我们首先看一个SQL-99中的定义,然后了解如何使用外部语言中的定义。有些数据库系统支持它们自己的过程语言,例如Oracle中的PL/SQL和Microsoft SQLServer中的TransactSQL。它们类似于SQL-99的有关过程的部分,但在语法和语义上有所区别,详细信息可参见各自的系统手册。
|
|
|
|
|
|
假定我们想要这样一个函数:给定一个书名,返回其作者数,使用4NF模式(参见上图)。我们可以用下面这种方式定义这个函数:
|
|
|
|
这个过程可以用在一个返回具有多于一个作者的所有书的名称的查询:
|
|
|
|
函数对于特定的数据类型(比如图像和几何对象)来说特别有用。例如,用在地图数据库中的一个多边形数据类型可能有一个相关函数用于判断两个多边形是否重叠,一个图像数据类型可能有一个相关函数用于比较两幅图的相似性。函数可以用外部语言(比如C)来编写。一些数据库系统也支持返回关系(即元组的多重集合)的函数,尽管SQL-99并不支持这些函数。
|
|
|
前面介绍的方法,可以看作与结构类型相关联的函数。它们的第一个参数self是隐含的,它被设置为调用这个方法的结构类型的值。因此,方法主体可以通过self.a来引用这个值的属性a。这些属性也可以被该方法更新。
|
|
|
SQL-99也支持过程。author-count函数也可以写成一个过程:
|
|
|
|
可以从一个SQL过程中或者从嵌入式SQL中使用call语句调用过程:
|
|
|
|
SQL-99允许多个过程同名,只要同名的不同过程的参数个数不同。名称和参数个数用于标识一个过程。SQL-99也允许多个函数同名,只要这些同名的不同函数的参数个数不同,或者,对于那些有相同个数参数的函数,至少有一个参数的类型不同。
|
|
|
|
SQL-99允许使用一种程序设计语言(如C或C++)定义函数。这种方式定义的函数会比SQL中定义的函数效率更高,无法在SQL中执行的计算可以由这些函数执行。
|
|
|
外部语言过程需要处理空值和异常。因此它们必须具有几个额外的参数:一个指明失败/成功状态的sqlstate值,一个存储函数返回值的参数,和一些指明每个参数/函数结果的值是否为空的指示器变量。在声明语句的上方添加额外的一行parameter style general指明外部过程/函数只使用说明的变量并且不处理空值和异常。
|
|
|
|
SQL-99支持多种过程的构造,这赋予它与通用程序设计语言相当的几乎所有的功能。SQL-99标准中处理这些构造的部分称为持久存储模块(persistent stroage module,PSM)。
|
|
|
|
一个复合语句有begin …end的形式,在begin和end之间会包含多条SQL语句。SQL-99支持while语句和repeat语句,语法如下:
|
|
|
|
这段代码没什么用,只是说明while和repeat循环的语法。
|
|
|
|
|
|
程序在for循环开始执行的时候隐式地打开一个游标,并且用它每次获得一个行的值存入for循环变量(在上面例子中指r)中。赋予该游标一个名称是有可能的,可通过在关键字as后插入文本cn cursor for完成,其中的cn是游标的名称。游标名可用于对该游标指向的元组进行更新/删除操作。语句leave可用来退出循环,而iterate表示跳过剩余语句从循环的开始进入下一个元组。
|
|
|
|
条件语句包括if-then-else语句,使用语法如下:
|
|
|
|
这段代码假定1、m和h是整型变量,r是行变量。如果我们用这段if-then-else代码替换掉前面一段的for循环中的“set n=n+r.balance”行,则循环将按照低、中和高余额分别计算这三类账户余额的总数。
|
|
|
SQL-99的case语句类似于C/C++语言中的case语句。
|
|
|
SQL-99还包括发信号通知异常条件(exception condition)的概念,以及声明句柄(handler)来处理异常,代码如下:
|
|
|
|
在begin和end之间的语句可以执行signal out-of-stock来引发一个异常。这个句柄说明,如果条件发生,将会采取动作终止begin end中的语句。替换的动作将是continue,它继续从引发异常的语句的下一条语句开始执行。除了明确定义的条件,还有一些预定义的条件,比如sqlexception、sqlwarning和not found。
|
|
|
下图是有关SQL-99的过程构造的实例。过程findEmpl计算给定经理(由参数mgr指定)的所有直接和非直接管理的员工的集合,并且将结果集中员工名字存储在一个被称为empl的关系中(假定这个关系已经存在)。关系manager(empname,mgrname)(假定可用)指明了谁直接为哪个经理工作。所有的直接/非直接管理的员工的集合构成了manager关系的传递闭包的主体。
|
|
|
|
|
该过程使用了两个临时表:newemp和temp。该过程在repeat循环之前把所有直接为mgr工作的员工插入到newemp中。Repeat循环首先把newemp中的所有员工添加到empl中。然后,它计算newemp中为经理工作的员工(除了那些已经找到的mgr的员工)并把它们存储到临时表temp中。最后用temp中的内容替换newemp中的内容。Repeat循环在找不到新员工(非直接的)时终止。我们注意到except子句的用处,它确保即使在有管理的循环(反常的)情况下,该过程仍可工作。例如,如果a为b工作,b为c工作,而c又为a工作,就出现了循环。
|
|
|
虽然在管理控制中循环不可能出现,但是其他的应用中可能会出现循环。例如,假设我们有关系flights(to,from),表示从一个城市直飞可达的其他城市。我们可以修改findEmpl过程来查找从给定城市利用一次或多次航班的一个序列能到达的所有城市。所有我们要做的就是用flight替换manager并且替换相应的属性名。这种情况下有可能出现一个可到达性的循环,但是由于该过程排除了已经找到的城市,所以它能正确执行。
|
|
|