SPL(过程)语句
该主题描述存储过程语言(SPL)语句,用于编写 SPL 例程。您可以将这些例程作为用户定义的例程(UDR)存储在数据库中。
SPL 例程(之前称为存储过程)是用于控制 SQL 活动的有效工具。本章包含 SPL 语句的描述。每个语句的描述包含以下信息: 信息
- 说明语句的作用的简单介绍
- 显示如何正确输入语句的语法图表
- 说明语法图表中每个输入参数的语法表
- 使用规则,包括带有说明这些规则的示例
如果语句由多个子句组成,则该语句描述为每个子句提供相同的信息集合。
有关创建和使用 SPL 例程的 SPL 语言和面向任务的信息的概述,请参阅GBase 8s SQL 教程指南。
有关如果创建和使用 SPL 例程中预备对象和动态 SQL 的详细示例的概述,请参阅 GBase developerWorks 中的 GBase 8s 数据库服务器存储过程语言的动态 SQL 支持。
GBase 8s 可以使用 CREATE PROCEDURE 或 CREATE PROCEDURE FROM 语句创建 SPL 函数,但是它对与外部函数则需要使用 CREATE FUNCTION 或 CREATE FUNCTION FROM 语句。但是,建议您使用 CREATE FUNCTION 或 CREATE FUNCTION FROM 语句创建新的用户定义的函数。
调试 SPL 例程
可以使用 Routine Debugger 客户端应用程序标识并分析 SPL 中的逻辑错误。
您可以在 SPL 例程中包含 TRACE 语句来生成跟踪输出。有关如何生成及检查 TRACE 语句的输出,请参阅 TRACE。
调试 SPL 例程中的当前限制
以下软件产品可支持当前用于调试 GBase 8s SPL 例程的客户端环境:
- Optim™ Development Studio (ODS)
- GBase Database Add-Ins for Visual Studio (IDAIVS)
有关哪些 GBase 8s 数据类型是只读的以及哪些是可更新的信息,请参阅数据类型支持文档。
以下限制适用于 Optim Development Studio (ODS) 和 GBase Database Add-Ins for Visual Studio (IDAIVS) 调试环境:
未日志记录的数据库
GBase 8s 非事务型数据库不支持 SPL 调试。无法对 ODS 或 IDAIVS 调试环境使用省略 WITH LOG 关键字的 CREATE DATABASE 语句的 GBase 8s 数据库。
辅助服务器实例
集群环境中的辅助服务器不支持 Insert 、Delete 或 Update 触发器的 'Step into' 触发过程操作。
用引号(")分隔的数据字符串
ODS 或 IDAIVS 调试客户端当前对引号标记分隔符的解释可能与某些 SPL 例程的预期行为冲突。
- 目前,在 ODS 调试会话中,缺省情况下,GBase Data Server 驱动程序 JDBC 和 SQLJ 连接字符串中的 DELIMIDENT 环境变量设置为'y'。JDBC 连接的 DELIMIDENT 缺省值为'n'。
- 在当前 IDAIVS 调试会话中,缺省情况下, GBase 8s .NET provider 连接字符串中的 DELIMIDENT 环境变量设置为 'y' 。
DELIMIDENT 环境变量的设置会影响数据库服务器如何解释带引号的字符串:
- 'y' 指定由双引号(")标记包括的字符串是分隔的 SQL 标识符。客户端应用程序必须使用单引号(')标记分隔字符串,并且只能在分隔的 SQL 标识符周围使用双引号("),可以支持比在未限定标识符中有效的更大的字符集。在支持区分大小写的语言环境中,分隔字符串中或分隔标识符内的字母都区分大小写。这是 .NET 的缺省值。
- 'n' 指定客户端应用程序可以使用双引号(")或单引号(')标记分隔字符串,但是不能分隔 SQL 标识符。如果数据库服务器遇到在需要 SQL 标识符的上下文中由双引号或单引号分隔的字符串,则它会发出错误。限定 SQL 标识符的所有者名称可以用单引号(')标记分隔。您必须使用一对相同的引号符号来分隔字符串。
- 如果客户端系统上没有指定 DELIMIDENT 值,则使用缺省设置。如上所述,对于 OCD 该缺省值为 'n'(来自GBase Data Server 驱动程序 JDBC 和 SQLJ 连接字符串)。对于 IDAIVS ,该缺省值为 'y'(来自 GBase 8s .NET provider 连接字符串)。
只影响 ODS 调试会话的限制
缺省情况下,JDBC 和 SQLJ 的 GBase Data Server 驱动程序的 'AUTOCOMMIT' 事务方式设置为 'TRUE' 。它会在所有的日志记录的数据库的 ODS 调试会话中启用,包括创建的 WITH LOG MODE ANSI 的数据库,以及不兼容 ANSI 的数据库。
目前, GBase 8s 服务器在 Optim Development Studio 调试会话中完成每个 SQL 语句后执行隐式提交操作。如果在 GBase 8s SPL 例程中启动了显式事务,则数据库服务器将忽略 SQL ERROR -535 并继续调试会话。
只影响 IDAIVS 调试会话的限制
目前,GBase Database Add-Ins for Visual Studio 不支持 SPL 函数调试。您只能将 IDAIVS 调试环境用于不向调用上下文返回任何值的 GBase 8s SPL 过程。
使用 Optim Development Studio 开始 SPL 调试会话
要使用 Optim™ Development Studio (ODS) 调试 SPL 例程,您必须在 GBase 8s 数据库服务器和 ODS 客户端之间建立连接。
您可以在客户端调试环境中检查 SPL 例程的运行时的行为,并用 Optim Development Studio(ODS)的标准调试器接口控制调试会话的流程。以下步骤也适用于 Optim Data Studio。
GBase 8s 数据库服务器的 SPL Routine Debugger 支持在 ODS 2.2.1.0 及以后的版本中有效。 早期的 ODS 版本(如 2.2.0)支持 GBase 8s 数据库服务器,但是不提供 GBase 8s SPL 支持和 SPL Routine Debugger 支持。
要在 ODS 中启用 SPL Routine 调试会话,请遵循以下步骤:
GBase 8s 服务器实例:启动和配置
要配置 GBase 8s 数据库服务器实例以致于启用 SPL 例程调试,请按照下列步骤操作:
-
安装 GBase 8s 数据库服务器产品。
-
配置 onconfig 和 sqlhosts 文件中的条目并启动 GBase 8s 服务器,以支持 DRDA 通信协议:
- onconfig:
DBSERVERNAME ids_spldb
- sqlhosts:
ids_spldb drsoctcp ids_server_machine_name port_number
注意GBase 8s 数据库服务器的 SPL Routine Debugger 支持通过 JDBC 和 SQL 的 GBase Data Server 驱动连接到客户端,并且需要 GBase 8s DRDA 协议连接。
-
您必须提供一个 sbspace ,以便数据库服务器存储 ODS 发送给服务器的 XML 消息。SBSPACENAME 配置参数的设置指定了系统缺省 sbspace 的名称。可以通过带 -Df "LOGGING=ON" 选项的 onspaces 实用程序在数据库上创建缺省 sbspace。此 sbspace 在 ODS 上调试 SPL 的必要条件。
-
如果这是新的服务器实例,使用 CREATE DATABASE 语句创建新的数据库。稍后将使用此数据库在 ODS 中创建数据库连接,以部署和调试 SPL 例程。
启动例程调试器会话管理器(可选的)
在大多数情况下,您可以使用 ODS 提供的内置的会话管理其调试 GBase 8s SPL 例程。但是在某些情况中(例如,如果您的服务器机器在防火墙之后),您可能需要在 TCP/IP 端口上启动会话管理器,该端口在服务器计算机或其它计算机上具有出站访问权限。
要手动启动会话管理器,请按照下列步骤操作:
-
要手动启动会话管理器,使用以下的命令输出 CLASSPATH 环境变量设置:
export CLASSPATH=${GBASEDBTDIR}/bin/db2dbgm.jar:$CLASSPATH
-
使用 Java™ 1.5.0 或更高版本并确保它在 PATH 环境变量中运行下列命令,指定会话管理器将会使用的 port number 以及会话管理器日志文件的 pathname :
java com.gbase.db2.psmd.mgr.Daemon -port port_num -log sess_mgr_log_path
在 ODS 中部署和调试 SPL 例程的步骤
要配置 ODS 以部署和调试 GBase 8s SPL 例程,请按照下列步骤操作:
- 如果不使用内置会话管理器并启动单个的会话管理器,请使用菜单命令在 ODS 中配置该选项(Window > Preferences > Run/Debug > Routine Debugger > DB2 screen)方法是选择 "Use an already running Session manager" 单项按钮,并提供端口号和机器名称。
- 要配置 ODS 以调试 GBase 8s SPL 例程,您需要在 ODS 的 Data Source Explorer 中创建一个 Database 连接。有关如何创建 Database 连接的详细信息请参阅 Optim Development Studio 文档。
- 在 Database Connection > New Connection 对话框中右击。选择 GBase 8s ,并通过从驱动程序列表中选择对 GBase 8s 适当版本的 JDBC 和 SQLJ GBase Data Server 驱动程序来创建新的数据库连接。
- 在 Edit Driver Definitions 对话框中,打开 Jar List 选项卡并验证它是否包含 db2jcc4.jar。如果没有,在Driver files: 列表中使用 db2jcc4.jar 替换 db2jcc.jar 。
- 有关如何 Create 、Deploy 和 Debug SPL 例程的详细信息请参阅 Optim Development Studio 文档。
现在您可以准备在 Optim Development Studio 中创建、部署并调试 GBase 8s SPL 例程。
使用 GBase Database Add-Ins for Visual Studio 调试 SPL 过程
可以使用 GBase Database Add-Ins for Visual Studio 调试 GBase 8s SPL 过程。
您可以调试运行在 GBase 8s 上的任何 SPL 过程。您可以遍历您的代码,设置行或变量断点,查看变量值,更改变量值,查看嵌套过程的调用堆栈信息,以及在调用堆栈的不同过程之间切换。通过在调试模式下运行过程并查看结果时单步执行代码,可以发现过程的问题并进行必要的更改。
要在 GBase Database Add-Ins for Visual Studio(IDAIVS)中启动 SPL 例程调试,请按照以下步骤操作:
启动并配置 GBase 8s 数据库服务器实例
要配置 GBase 8s 数据库服务器实例以致于启用 SPL 例程调试,请按照下列步骤操作:
-
安装 GBase 8s 数据库服务器产品。
-
配置 onconfig 和 sqlhosts 文件中的条目并启动 GBase 8s 服务器,以支持 DRDA 通信协议:
- onconfig:
DBSERVERNAME ids_spldb
- sqlhosts:
ids_spldb drsoctcp ids_server_machine_name port_number
注意GBase 8s 的 SPL Routine Debugger 支持通过 GBase 8s .Net provider 连接到客户端,并且需要 GBase 8s DRDA 协议连接。
-
您必须提供一个 sbspace ,以便数据库服务器存储 GBase Database Add-Ins for Visual Studio 发送给服务器的 XML 消息。SBSPACENAME 配置参数的设置指定了系统缺省 sbspace 的名称。可以通过带 -Df "LOGGING=ON" 选项的 onspaces 实用程序在数据库上创建缺省 sbspace。此 sbspace 在 GBase Database Add-Ins for Visual Studio上调试 SPL 的必要条件。
-
如果这是新的服务器实例,使用 CREATE DATABASE 语句创建新的数据库。稍后将使用此数据库在 GBase Database Add-Ins for Visual Studio 中创建数据库连接,以部署和调试 SPL 例程。
启动例程调试会话管理器
要启动会话管理器,请按照下列步骤操作:
-
要手动启动会话管理器,使用以下的命令输出 CLASSPATH 环境变量设置:
export CLASSPATH=${GBASEDBTDIR}/bin/db2dbgm.jar:$CLASSPATH
-
使用 Java™ 1.5.0 或更高版本并确保它在 PATH 环境变量中运行下列命令,指定会话管理器将会使用的 port number 以及会话管理器日志文件的 pathname :
java com.gbase.db2.psmd.mgr.Daemon -port port_num -log sess_mgr_log_path
该会话管理器必须在运行 GBase 8s 实例的机器上启动,这也是 GBase Database Add-Ins for Visual Studio 连接到会话管理器的必要条件。
建立 GBase 8s 数据库服务器连接
通过选择 "Add Connection…" 菜单项创建新连接,可以从服务器资源管理器中 "Data Connections" 节点上的操作(右键单击)弹出菜单访问 Add Connection 对话框。Data source 字段会显示 " GBase 8s and U2 Servers (GBase 8s and U2 Data Provider)。"
- 在下面 "1. Select or enter server name:"字段中,输入 GBase 8s 服务器实例的名称。
- 在 "2. Enter information to log on to the server:" 旁边的字段中输入您的用户 ID 作为 User ID: ,在 Password: 字段中输入服务器上 gbasedbt 账户的密码。
- 在"3. Select or enter a database name:" 下面的字段中,输入存储要调试的 SPL 过程的数据库的名称。
- 点击 Test Connection 按钮以测试 GBase 8s 服务器实例的连接。
- 如果出现 Test connection succeeded 对话框,您可以点击 OK 按钮。可以忽略此 Add Connection 对话框,因为您已经在 GBase 8s 服务器和 Visual Studio 之间创建了连接。
设置会话管理器
在您开始调试会话之前,需要指定要调试的 SPL 例程,和数据库服务器连接以及会话管理器的端口号。
- 从 Server Explorer 中的 "Data Connections" 节点中,单击所有连接的 GBase 8s 服务器。
- 展开 Procedures 标题以显示数据库中所有存储过程的名称。
- 单击您要调试的 SPL 例程的名称。
- 从 Tools 选项卡,选择 Tools 列表底部附近的 "Options . . . " 菜单。
- 从 Options 对话框中,展开 GBase 8s Database Tools 项,然后单击 General 项显示类别的列表。
- 使用 General 选项列表右侧的滑块控件显示 Session Manager Connection 和 Session Manager Port 号。
- 单击 OK 接受会话管理器配置的这些值。
有关如何使用 Visual Studio 的调试 SPL 例程的详细信息,请参阅 GBase Database Add-Ins for Visual Studio。
调试 GBase 8s SPL 过程
这是使用 GBase Database Add-Ins for Visual Studio 调试 SPL 过程的步骤:
-
启用该过程的调试:
- 在数据连接的服务器资源管理器中,右键点击要调试的过程,然后单击快捷菜单上的 Open Definition 。
- 在 GBase Procedure Designer 的过程视图中,在 Debug mode 文件中选择 ALLOW。
- 保存此过程,但是保持 Designer 打开。
-
在 GBase Procedure Designer 中设置行断点。
- 如果过程在 Designer 中未打开,请在服务器资源管理器中的数据连接下,右键单击该过程,然后单击快捷菜单上的 Open Definition 。
- 在 Designer 的“过程”视图的 SQL Body 部分中,设置行断点。
- 要设置断点的属性,请右键单击左边距中的断点,选择快捷菜单上的 Location 、Filter 或 When Hit ,并在打开的窗口中指定必要的信息。
-
在调试模式中开始运行此过程。
- 如果该过程在 GBase Procedure Designer 中打开,请单击 GBase Procedure Designer 选项卡上的 Step Into。
- 如果未在 Designer 中打开此过程,在资源管理器中右击此过程,然后单击快捷菜单上的 Step Into。
-
在调试模式下运行每个过程,并使用以下方法之一:
- 设置变量断点。在 SQL 主体中,右击变量名称,在快捷菜单上点击 Breakpoints ,并选择 Insert Variable Breakpoints。
- 修改断点的值。
-
继续调试 GBase 8s SPL 过程直到该过程返回期望的结果。
在 CLR 应用程序中调试 GBase 8s SPL 过程
当您以 C# 和 Visual Basic 语言开发 Windows™ 或 ASP.NET 应用程序,您可以使用 GBase Database Add-Ins for Visual Studio调试公共语言运行库(CLR)应用程序。 如果该应用程序访问 GBase 8s 数据库服务器实例,则您可以在调试此应用程序时调试从此应用程序调用的 SPL 过程。
GBase Unified Debugger 通过 GBase Procedure Designer 调试 SPL 过程。如果当您调试应用程序时一个过程定义在 Designer 中打开,随着调试器进入该过程,将激活该 Designer 的实例。如果未在 Designer 中打开该过程定义,则随着调试器进入该过程,此调试器会在 Designer 的新实例中打开过程定义。
先决条件: 要调试 CLR 应用程序的 SPL 过程,在包含此应用程序中的项目中启用 GBase 8s SQL debugging 。
要调试 CLR 应用程序的 SPL 过程,请按照以下步骤操作:
-
对于应用程序中的每个 SPL 过程:
- 启用此过程的调试:
- 在数据连接的服务器资源管理器中,右键点击要调试的过程,然后单击快捷菜单上的 Open Definition 。
- 在 GBase Procedure Designer 的过程视图中,在 Debug mode 文件中选择 ALLOW。
- 保存此过程,并且可以选择打开 Designer 以设置行断点。
- 可选:在 GBase Procedure Designer 中设置行断点。
- 在 Designer 的“过程”视图的 SQL Body 部分中,设置行断点。
- 要设置断点的属性,请右键单击左边距中的断点,选择快捷菜单上的 Location 、Filter 或 When Hit ,并在打开的窗口中指定必要的信息。
- 保持 Designer 打开。
- 启用此过程的调试:
-
开始调试应用程序。
在解决方案资源管理器中,右击此应用程序,选择快捷菜单上的 Debug ,然后选择 Start new instance。
调试开始而且 Debugger Task Status 窗口打开。
通用调试的每个过程的调用堆栈与调用该过程的线程的调用堆栈合并。您可以从 C# 或 Visual Basic 代码中查看调用过程的位置。
-
在调试模式运行每个调用的过程。
- 如果在步骤 1a.iii 中关闭了 GBase Procedure Designer,在 Designer 的新实例中,在 SQL Body 部分为该过程设置行断点和断点属性。
- 设置变量断点。在 SQL 主体中,右键单击变量名称,单击快捷菜单上的 Breakpoints 然后选择 Insert Variable Breakpoints。
- 修改断点的值。
-
要取消长时间运行任务,请在 Debugger Task Status 窗口中单击 Cancel 。
如果在调试应用程序时关闭了窗口,请从 Tools 菜单,选择 Show GBase 8s Debugger Task Status 来重新打开此窗口。
-
继续调试 GBase 8s SPL 过程直到该过程返回期望的结果。
在 CLR 应用程序中启用 SQL 调试
如果要在 C# 或 Visual Basic 公共语言运行时(CLR)应用程序中调试 GBase 8s SPL 过程,则必须首先为 GBase 数据库项目启用 GBase SQL 调试。您只需执行一次此过程,为项目启用 SQL 调试会在 Visual Studio 中设置一个会在会话之间持续存在的项目属性。
当您在应用程序中启用 SQL 调试,则 GBase Unified Debugger 必须关闭并重新打开包含此应用程序的 GBase 数据库项目。项目重新打开后,必须指定数据连接以及可选的运行调试会话管理器的端口。您还必须更改某些标准 Visual Studio 项目调试属性的设置。
加载项( 32 位应用程序)不支持控制台应用程序的 64 位调试过程。在 64 位操作系统中,缺省的 debug 平台是 Any CPU ,这是一个 64 位调试过程。您必须在项目属性中指定 32 位调试平台。
先决条件: 要在应用程序中启用 GBase SQL 调试,在包含此应用程序的 GBase 数据库项目必须在解决方资源案管理中打开。
要在应用程序中启用 GBase SQL 调试,请按照以下步骤操作:
-
在解决方资源案管理中,右键单击 GBase 数据库项目节点,然后在快捷菜单中选择 Enable GBase 8s SQL Debugging。
将显示一条消息,说明必须关闭并重新打开项目。
-
在消息窗口中单击 Yes 以确认项目的关闭和重新打开。
保存所有未保存的修改,关闭项目,然后在解决方案资源管理器中重新打开它。
-
在解决方案资源管理器中,右键单击 GBase 数据库项目节点,然后在快捷菜单中选择 Properties 。
将打开 Project Designer 。
-
在 Designer 的左侧框,单击 GBase 8s Unified Debugger。
将显示 GBase Unified Debugger 页面。
-
执行以下一组操作:
- 如果您有以下连接之一用于调试会话管理器:
- GBase 8s 12.10
- Linux™ 、UNIX™ 和 Windows 的 DB2
- DB2 z/OS® 9.1 或 10 版本
- DB2 i V5R4 或 V6R1
- 选择 Use an existing connection for session manager。
- 在列表中,选择要在其上运行调试会话管理器的数据连接。
- 如果您正在手动运行此调试会话管理器:
- 选择 Use a new host name for session manager。
- 为此调试会话管理器指定主机名。
- 如果您有以下连接之一用于调试会话管理器:
-
如果您在 64 位的操作系统上调试控制台应用程序,请在 Platform 列表中选择 x86。
-
可选的:在 Session manager port 字段中,键入要在其上运行调试会话管理器的端口。
-
在项目设计器左侧框中, 单击 Debug。
将显示项目设计器的 Debug 页面。
-
在 Debug 页面修改以下属性:
- 在 Start Options,清除 Use remote machine 复选框。
- 在 Enable Debuggers,清除以下三个复选框:
- Enable unmanaged code debugging
- Enable SQL Server debugging
- Enable the Visual Studio hosting process
-
关闭 Project Designer。
CALL
使用 CALL 在 SPL 例程中执行用户定义的例程(UDR)。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
data_var | 用于接收 function 返回的值的变量 | data_var 的数据类型必须适合于已返回的值 | 标识符 |
function, procedure | 用户定义的函数或过程 | 函数或过程必须存在 | 标识符 |
routine_var | 包含 UDR 名称的变量 | 必须是包含现有 UDR 的非 NULL 名称的字符数据类型 | 标识符 |
用法
CALL 语句调用 UDR。CALL 语句对 EXECUTE PROCEDURE 和 EXECUTE FUNCTION 语句的行为是一致的,但仅可在 SPL 例程中使用 CALL 。
可以将 CALL 用在 GBase 8s ESQL/C 程序中或与 DB-Access 一起使用,但仅当该语句在执行了该程序或 DB-Access 的 SPL 例程中时。
当您使用 CALL 调用由其函数标识符指定的用户定义的函数或用于存储函数标识符的 routine_var 时,CALL 语句必须包含 RETURNING 子句。
CALL 语句不能从 SELECT 语句的 FROM 子句中的子查询调用迭代器 TABLE 函数。有关迭代器 TABLE 函数的语法,请参阅迭代器函数。
指定参数
CALL 语句的参数列表(由括号分隔)紧跟在 UDR 的名称后面。如果不包含执行参数,则空括号必须跟在 UDR 的名称后面。如果列表包含的参数超过 UDR 的参数,则会收到错误。
如果 CALL 指定的参数比 UDR 预期的少,则会指出缺少参数。数据库服务器将缺少的参数初始化为它们相应的缺省值。(请参阅 CREATE PROCEDURE 和 CREATE FUNCTION。)此初始化出现在 UDR 主体中的第一个可执行语句之前,如果缺少的参数没有缺省值,则它们被初始化为 UNDEFINED 的值。尝试使用 UNDEFINED 值的任何变量会导致错误。
在每个 UDR 调用中,您有指定传递给 UDR 的参数的参数名称的选择。下面每一个示例对于期望以该顺序命名为 t 、n 和 d 的字符参数是有效的:
CALL add_col (t='customer', n = 'newint', d ='integer');
CALL add_col('customer','newint','integer');
以上的 CALL 语句作用相同。
有关参数列表语法的详细信息,请参阅参数。
从被调用的 UDR 接收输入
RETURNING 子句指定变量,该变量接收已调用的函数返回的值。
以下示例显示两个 UDR 调用:
CREATE PROCEDURE not_much()
DEFINE i, j, k INT;
CALL no_args (10,20);
CALL yes_args (5) RETURNING i, j, k;
END PROCEDURE;
第一个例程调用(no_args)不期望有返回值。第二个例程调用是针对函数(yes_args)的,它期望三个返回值。not_much() 过程声明三个整数变量(i 、j 和 k)以接收来自 yes_args 的返回值。
CASE
当需要根据 SPL 变量或单一表达式的值从许多分支中取用一个时,使用 CASE 语句。CASE 语句是对 IF 语句的快速替换。
语法
ELSE 子句
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
constant_expr | 指定精确值的表达式 | 必须是精确数值、引用字符串、精确日期时间或精确时间间隔。数据类型必须与数据 value_expr 相一致 | 常量表达式 |
value_expr | 返回值的表达式 | SPL 变量或者返回值或 NULL 的其它类型的表达式。该数据类型不能是大对象(BLOB 、BYTE 、CLOB 、TEXT)、集合或者用户定义的 OPAQUE 或 DISTINCT 类型。在内置 OPAQUE 类型中,只有 BOOLEAN 和 LVARCHAR 是有效的。 | 表达式 |
用法
可以使用 CASE 语句在 SPL 例程中创建一组条件分支。WHEN 和 ELSE 子句都是可选的,但您必须提供其中一个或另一个。如果既不指定 WHEN 子句也不指定 ELSE 子句。则会收到语法错误消息。
- 如果不包含 WHEN 子句也不包含 ELSE 子句,则 CASE 语句由于语法错误而失败。
- 如果不包含 ELSE 子句,但是 WHEN 子句没有指定一个与 value_expr 相匹配的 constant expr t,则 CASE 语句在例程执行时产生错误 -26062 并失败。
不要将 CASE 语句和 SQL 的 CASE 表达式混淆。(CASE 表达式支持作为 CASE 语句的同一关键字。但是使用不同的语法和语义计算指定的 conditions 。CASE 表达式会返回一个值或 NULL,如 CASE 表达式 中描述。)
数据库服务器如何执行 CASE 语句
数据库服务器按一下操作的顺序的执行 CASE 语句:
- 数据库服务器计算 value_expr 表达式。
- 如果得到的值与在 WHEN 子句的 constant_expr 参数中指定的精确值相匹配,则数据库服务器执行跟随该 WHEN 子句中的 THEN 关键字的语句块。
- 如果计算 value_expr 参数得到的值与多个 WHEN 子句中的 constant_expr 参数相匹配,则数据库服务器执行跟随 CASE 语句中第一个匹配的 WHEN 子句中 THEN 关键字的语句块。(在这种情况中,WHEN 子句的词法顺序可以确定 CASE 语句的结果。)如果数据库服务器执行了跟随在 THEN 关键字之后的 GOTO 语句,则数据库服务器将程序控制传递给指定的语句标签。否则,数据库服务器执行 SPL 例程中标识当前 CASE 语句结束的 END CASE 关键字后面的下一个 SPL 语句或 SQL 语句。
- 如果 value_expr 参数计算的值与任何 WHEN 子句的 constant_expr 参数中指定的精确值不匹配,并且如果 CASE 语句包含 ELSE 子句,则数据库服务器执行跟随在 ELSE 关键字的语句块。如果数据库服务器执行了跟随在 ELSE 关键字之后的 GOTO 语句,则数据库服务器将程序控制传递给指定的语句标签。否则,数据库服务器执行 SPL 例程中标识当前 CASE 语句结束的 END CASE 关键字后面的下一个 SPL 语句或 SQL 语句。
- 如果 value_expr 参数计算的值与任何 WHEN 子句的 constant_expr 参数中指定的精确值不匹配,并且如果 CASE 语句不包含 ELSE 子句,则数据库服务器发出异常,并且 CASE 语句失败,错误 -26062。SPL 例程是否终止或继续执行取决于其异常处理逻辑。
CASE 语句的此实现非常类似于GBase 8s Parallel Server 的实现,除了当未指定 ELSE 子句且没有 WHEN 子句与 value_expr 参数相匹配时,GBase 8s Parallel Server 不会发生错误。在这种情况下,程序执行将在紧跟 CASE 语句之后的 SPL 或 SQL 语句中继续。
更随 THEN 或 ELSE 关键字的语句块可以包括在 SPL 例程的语句块中有效的任何 SQL 语句或 SPL 语句。有关更多信息,请参阅语句块。
在 CASE 语句中值表达式的计算
数据库服务器仅计算一次 value_expr 参数的值。它在开始执行 CASE 语句时计算此值。如果该参数中的指定表达式包含一个或多个 SPL 变量。并且这些变量中的任何变量的值随后在 CASE 语句中的一个语句块中修改,则数据库服务器不会重新计算 value_expr 参数的值。因此,在 value_expr 参数中指定的变量值的任何更改都不好影响 CASE 语句采用的分支。
CASE 语句的示例
在以下示例中,CASE 语句根据另一个命名为 i 的 SPL 变量的值将 SPL 变量集合(命名为 j 、k 、l 和 m)之一初始化为命名为 x 的 SPL 变量的值:
CASE i
WHEN 1 THEN LET j = x;
WHEN 2 THEN LET k = x;
WHEN 3 THEN LET l = x;
WHEN 4 THEN LET m = x;
ELSE
RAISE EXCEPTION 100; --invalid value
END CASE;
此处每个 WHEN 子句指定一个整数作为它的常量表达式,假设值表达式具有数据类型。(如果这些精确值已用引号分隔,则数据库服务器将它们视为字符值。)
下面的示例包含 WHEN 子句中对 NULL 的测试,其表达式的值和常量表达式的数据类型为 CHAR(1):
CREATE PROCEDURE case_proc( )
RETURNING CHAR(1);
DEFINE grade CHAR(1);
LET grade = 'D';
CASE grade
WHEN 'A' THEN LET grade = 'a';
WHEN 'B' THEN LET grade = 'b';
WHEN 'C' THEN LET grade = 'c';
WHEN NULL THEN LET grade = 'z';
ELSE LET grade = 'd';
END CASE;
RETURN grade;
END PROCEDURE;
CONTINUE
使用 CONTINUE 语句启动最里面的 FOR 、LOOP 、WHILE 或 FOREACH 循环的下一个迭代。
语法
用法
当执行控制传递到 CONTINUE 语句时,SPL 例程跳过指定类型的最里面循环中的其余语句。执行在顶层循环继续下一个迭代。
在以下示例中,loop_skip 函数将值 3 到 15 插入到表 testtable 中。该函数还在此过程中返回 3 到 9 和 13 到 15 。该函数不返回值 11 因为它遇到 CONTINUE FOR 语句。CONTINUE FOR 语句使函数跳过 RETURN WITH RESUME 语句:
CREATE FUNCTION loop_skip()
RETURNING INT;
DEFINE i INT;
...
FOR i IN (3 TO 15 STEP 2)
INSERT INTO testtable values(i, null, null);
IF i = 11
CONTINUE FOR;
END IF;
RETURN i WITH RESUME;
END FOR;
END FUNCTION;
就像 EXIT 语句一样(EXIT),FOREACH 语句和 FOR 或 WHILE 语句不包含 LOOP 关键字,FOR 、WHILE 或 FOREACH 关键字必须紧跟在 CONTINUE 关键字之后来指定循环的类型。如果指定的循环类型与 CONTINUE 语句发出的上下文不匹配,则生成错误。
在 LOOP 、FOR LOOP 和 WHILE LOOP 语句中,不管标签的还是未标记的,关键字指示 CONTINUE 关键字之后的循环的类型是可选的,但是,如果您指定与循环类型不对应的关键字,则 GBase 8s 发出错误。
CONTINUE
使用 CONTINUE 语句启动最里面的 FOR 、LOOP 、WHILE 或 FOREACH 循环的下一个迭代。
语法
用法
当执行控制传递到 CONTINUE 语句时,SPL 例程跳过指定类型的最里面循环中的其余语句。执行在顶层循环继续下一个迭代。
在以下示例中,loop_skip 函数将值 3 到 15 插入到表 testtable 中。该函数还在此过程中返回 3 到 9 和 13 到 15 。该函数不返回值 11 因为它遇到 CONTINUE FOR 语句。CONTINUE FOR 语句使函数跳过 RETURN WITH RESUME 语句:
CREATE FUNCTION loop_skip()
RETURNING INT;
DEFINE i INT;
...
FOR i IN (3 TO 15 STEP 2)
INSERT INTO testtable values(i, null, null);
IF i = 11
CONTINUE FOR;
END IF;
RETURN i WITH RESUME;
END FOR;
END FUNCTION;
就像 EXIT 语句一样(EXIT),FOREACH 语句和 FOR 或 WHILE 语句不包含 LOOP 关键字,FOR 、WHILE 或 FOREACH 关键字必须紧跟在 CONTINUE 关键字之后来指定循环的类型。如果指定的循环类型与 CONTINUE 语句发出的上下文不匹配,则生成错误。
在 LOOP 、FOR LOOP 和 WHILE LOOP 语句中,不管标签的还是未标记的,关键字指示 CONTINUE 关键字之后的循环的类型是可选的,但是,如果您指定与循环类型不对应的关键字,则 GBase 8s 发出错误。
DEFINE
使用 DEFINE 语句声明 SPL 例程使用的本地变量,或声明可由几个 SPL 例程共享的全局变量。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
column | 列名 | 必须已经存在于 table 或 view 中 | 标识符 |
data_type | SPL_var 的类型 | 请参阅声明全局变量 | 数据类型 |
distinct_type | Distinct 类型 | 必须已经在数据库中定义 | 数据类型 |
opaque_type | Opaque 类型 | 必须已经在数据库中定义 | 数据类型 |
SPL_var | 新 SPL 变量 | 在语句块中必须是唯一的 | 标识符 |
synonym, table, view | 表、视图或同义词的名称 | 当发出 DEFINE 时它所指向的同义词和表或视图必须存在 | 标识符 |
用法
DEFINE 语句不是可执行语句。DEFINE 语句必须出现在例程头之后,任何其它语句之前。如果声明一个局部变量(通过使用不带 GLOBAL 关键字的 DEFINE),则它的引用作用域是定义了此局部变量的语句块。您可以在该语句块中使用该变量。在语句块之外具有不同定义的另一个变量可有相同的名称。
具有 GLOBAL 关键字的变量在作用域中是全局的并且在语句块之外且对于其它 SPL 例程是可用的。全局变量可以是除 BIGSERIAL 、BLOB 、BYTE 、CLOB 、SERIAL 、SERIAL8 或 TEXT 之外的任何内置数据类型。本地变量可以是除 BIGSERIAL 、BYTE 、SERIAL 、SERIAL8 或 TEXT 之外的任何内置数据类型。如果 column 是 BIGSERIAL 、SERIAL 或 SERIAL8 数据类型,则声明 BIGINT 、INT 或 INT8 变量(分别地)以存储其值。
将 SQL 关键字的名称或其它数据库对象的标识符声明为 SPL 变量会在某些上下文中产生错误或意外结果。有关涉及 SPL 变量的名称冲突的一些潜在问题的讨论,请参阅下面的相关概念。
引用 TEXT 和 BYTE 变量
REFERENCES 关键字使您能够使用 BYTE 和 TEXT 变量。这些不包括实际数据,但是是实际数据的指针。REFERENCES 关键字表示 SPL 变量仅仅是一个指针。您可以使用 BYTE 和 TEXT 变量就像您使用 SPL 中的任何其它变量一样。
重新声明或重新定义
如果您在同一语句块中将相同变量定义两次,则会收到错误消息。您可以在嵌套块中重新定义变量,在此情况下它临时隐藏外层声明。以下示例会产生错误:
CREATE PROCEDURE example1()
DEFINE n INT; DEFINE j INT;
DEFINE n CHAR (1); -- 重定义产生错误
重新声明在以下示例中是有效的。在嵌套语句块中,n 是一个字符变量。在块之外,n 是一个整型变量。
CREATE PROCEDURE example2()
DEFINE n INT; DEFINE j INT;
...
BEGIN
DEFINE n CHAR (1); -- character n masks global integer variable
...
END;
声明全局变量
语法:
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
data_type | SPL_var 的类型 | 请参阅 声明全局变量. | 数据类型 |
SPL_var | 新的 SPL 变量 | 在语句块中必须是唯一的 | 标识符 |
GLOBAL 关键字表示跟随的变量的引用作用域包含所有在给定的 DB-Access 或 SQL 管理 API 会话中运行的 SPL 例程。这些变量的数据类型必须与全局环境中的变量数据类型相匹配。全局变量是由所有 SPL 例程使用的内存。这些例程在给定的 DB-Access 或 SQL 管理 API 会话中运行。全局变量的值存储在内存中。
在当前会话中运行的 SPL 例程共享全局变量。因为数据库服务器不将全局变量保存在数据库中,所有当前会话关闭时不保留全局变量。
全局变量的第一个声明建立全局环境中的变量;后续全局声明只是将该变量绑定到全局环境并在此时建立变量的值。
以下示例显示两个 SPL 过程,proc1 和 proc2;每个过程都已定义了全局变量 gl_out:
- SPL procedure proc1
CREATE PROCEDURE proc1()
...
DEFINE GLOBAL gl_out INT DEFAULT 13;
...
LET gl_out = gl_out + 1;
END PROCEDURE;
- SPL procedure proc2
CREATE PROCEDURE proc2()
...
DEFINE GLOBAL gl_out INT DEFAULT 23;
DEFINE tmp INT;
...
LET tmp = gl_out
END PROCEDURE;
如果首先调用 proc1,则 gl_out 被设置为 13,然后增加为 14。如果接下来调用 proc2,它会发现 gl_out 已被定义,这样就不应用缺省值 23 。然后,proc2 将现有值 14 分配给 tmp。如果首先调用了 proc2 ,则 gl_out 会被设置为 23,并且 23 可能被分配给 tmp。以后对 proc1 的调用将不应用缺省值 13。
不同数据库服务器实例的数据库不共享全局变量,但是在单个会话中,同一个数据库服务器实例的所有数据库可以共享全局 SPL 变量。然而,数据库服务器和任何应用程序开发工具都不共享全局变量。
缺省值
全局变量可以拥有文字值、NULL值 或者系统常量缺省值。
缺省值
如果您指定缺省值,则全局变量使用指定的值初始化。
CURRENT
CURRENT 是仅用于 DATETIME 变量的有效缺省值。如果 YEAR TO FRACTION(3) 是其声明的精度,则不需要限定符。否则,当 CURRENT 是缺省值时必须指定相同的 DATETIME 限定符,如以下 DATETIME 变量的示例所示:
DEFINE GLOBAL d_var DATETIME YEAR TO MONTH
DEFAULT CURRENT YEAR TO MONTH;
SYSDATE
SYSDATE 是仅用于 DATETIME 变量的有效缺省值。如果 YEAR TO FRACTION(5) 是其声明的精度,则不需要限定符。否则,当 SYSDATE 是缺省值时必须指定相同的 DATETIME 限定符,如以下 DATETIME 变量的示例所示:
DEFINE GLOBAL dt_var DATETIME YEAR TO DAY
DEFAULT SYSDATE YEAR TO DAY;
USER
如果使用 USER 的值,或者它的同义词 CURRENT_USER 返回的值作为缺省值,则变量必须定义为 CHAR 、VARCHAR 、NCHAR 或 NVARCHAR 数据类型。建议变量的长度最少为 32 个字节。如果变量长度太短而无法存储缺省值,则在 INSERT 和 ALTER TABLE 操作期间会有得到错误消息的风险。
TODAY
如果您使用 TODAY 作为缺省值,则该变量必须是 DATE 值。(有关 TODAY 和可出现在缺省值子句中的其它系统常量的描述,请参阅常量表达式。)
BYTE 和 TEXT
对于 BYTE 或 TEXT 变量,唯一有效的缺省值是 NULL。以下示例定义称为 l_blob 的 TEXT 全局变量:
CREATE PROCEDURE use_text()
DEFINE i INT;
DEFINE GLOBAL l_blob REFERENCES TEXT DEFAULT NULL;
...
END PROCEDURE
这里需要 REFERENCES 关键字,因为 DEFINE 语句无法直接声明 BYTE 或 TEXT 数据类型;l_blob 变量是指向存储在全局环境中的 TEXT 值的指针。
SITENAME 或 DBSERVERNAME
如果您声明 SITENAME 或 DBSERVERNAME 关键字作为缺省值,则变量必须是 CHAR 、VARCHAR 、NCHAR 、NVARCHAR 或 LVARCHAR 数据类型。它的缺省值是数据库服务器在运行时的名称。建议该变量的大小至少为 128 个字节。如果该变量的长度太短而无法存储缺省值,则在 INSERT 和 ALTER TABLE 操作期间会有得到错误消息的风险。
以下示例使用 SITENAME 关键字指定缺省值。该示例还将 BYTE 全局变量初始化为 NULL:
CREATE PROCEDURE gl_def()
DEFINE GLOBAL gl_site CHAR(200) DEFAULT SITENAME;
DEFINE GLOBAL gl_byte REFERENCES BYTE DEFAULT NULL;
...
END PROCEDURE
声明本地变量
本地变量有其引用作用域,在该作用域中声明例程。如果省略 GLOBAL 关键字,则在 DEFINE 语句中声明的任何变量都是本地变量,并且在其它 SPL 例程中不可见。
出于此原因,声明相同名称的本地变量的不同 SPL 例程可在同一 DB-Access 或 SQL 管理 API 会话中运行而无冲突。
如果本地变量和全局变量具有相同的名称,那么全局变量在声明了本地变量的 SPL 例程中不可见。(在所有其它 SPL 例程中,只有全局变量在作用域中。)
语法:
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
column | 列名 | 必须已经存在于 table 或 view 中 | 标识符; |
data_type | SPL_var 的类型 | 不能是 BIGSERIAL 、BYTE 、SERIAL 、SERIAL8 或 TEXT | 数据类型 |
distinct_type | Distinct 类型 | 必须已经在数据库中定义 | 标识符 |
opaque_type | Opaque 类型 | 必须已经在数据库中定义 | 标识符 |
SPL_var | 新的 SPL 变量 | 在语句块中必须是唯一的 | 标识符; |
synonym, table, view | 表、视图或同义词的名称 | 当发出此语句时它所指向的同义词和表或视图必须存在 | 数据库对象名 |
本地变量不支持缺省值。以下示例显示本地变量的典型定义:
CREATE PROCEDURE def_ex()
DEFINE i INT;
DEFINE word CHAR(15);
DEFINE b_day DATE;
DEFINE c_name LIKE customer.fname;
DEFINE b_text REFERENCES TEXT;
END PROCEDURE
复杂数据类型的子集
可以使用以下语法将 SPL 变量声明为已归类的或类属集合,或者已命名的、未命名的或类属 ROW 数据类型。
复杂数据类型(子集)
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
data_type | 集合元素的类型或未命名的 ROW 类型字段的类型 | 必须与变量将存储的值的数据类型相匹配。不能是 BIGSERIAL、 BLOB 、BYTE 、CLOB 、SERIAL 、SERIAL8 或 TEXT。 | 数据类型 |
field | 未命名的 ROW 字段 | 必须在数据库中存在 | 标识符 |
row | 已命名的 ROW 数据类型 | 必须在数据库中存在 | 标识符 |
声明集合变量
类型为 COLLECTION 、SET 、MULTISET 或 LIST 的本地变量可保留取自数据库的值的集合,您不能(使用 GLOBAL 关键字)或使用缺省值将集合变量定义为全局(用于关键字)。
使用 COLLECTION 关键字声明的变量是可保留任何数据类型集合的未归类的(或类属)集合变量。
已声明为 SET 、MULTISET 或 LIST 类型的变量是已归类的集合变量。它只能保留指定的数据类型的集合。
当您定义已归类的集合变量的元素时,必须使用 NOT NULL 关键字,如下例所示:
DEFINE a SET ( INT NOT NULL );
DEFINE b MULTISET ( ROW ( b1 INT,
b2 CHAR(50)
) NOT NULL );
DEFINE c LIST( SET( INTEGER NOT NULL ) NOT NULL );
通过变量 c,SET 中的 INTEGER 值和 LIST 中的 SET 值都被定义为 NOT NULL。
您可以使用嵌套的复杂类型定义集合变量以保留匹配的嵌套复杂类型数据。允许任何类型或深度的嵌套。您可以在集合类型中嵌套 ROW 类型,在 ROW 类型中嵌套集合类型,在集合类型中嵌套集合类型,在集合和 ROW 类型中嵌套 ROW 类型,等等。
如果您将某个变量声明为 COLLECTION 类型,并且在相同的语句块中被重新指定,则该变量获取各不相同的数据类型声明,如下例所示:
DEFINE a COLLECTION;
LET a = setB;
...
LET a = listC;
在此示例中,varA 是一个类属集合变量,它将其数据类型更改为当前指定的集合的数据类型。第一个 LET 语句使 varA 成为 SET 变量。第二个 LET 语句使 varA 成为 LIST 变量。
声明 ROW 变量
ROW 变量保留从命名的或未命名的 ROW 类型的数据。您可以定义类属 ROW 变量,已命名的 ROW 变量或未命名的 ROW 变量。
使用 ROW 关键字定义的类属 ROW 变量可以保留来自任何 ROW 类型的数据。已命名的 ROW 变量保留来自变量声明中指定的已命名的 ROW 类型的数据。
以下语句显示了类属 ROW 变量和已命名的 ROW 变量的示例:
DEFINE d ROW; -- generic ROW variable
DEFINE rectv rectangle_t; -- named ROW variable
已命名的 ROW 变量保留在变量声明中相同类型的已命名的 ROW 类型。
要定义一个变量(该变量将保留存储在未命名的 ROW 类型中的数据),请使用后面跟有 ROW 类型的字段的 ROW 关键字,如:
DEFINE area ROW ( x int, y char(10) );
未命名的 ROW 类型是仅由等同结构检查的类型。如果两个未命名的 ROW 类型具有相同的字段数量,并且具有相同的类型定义,那么这两个 ROW 类型被认为是等同的。因此,您可以将以下 ROW 类型中任意一个访存到上面定义的变量 area 中:
ROW ( a int, b char(10) )
ROW ( area int, name char(10) )
就像 ROW 类型可以有字段一样,ROW 变量也可以有字段。要给 ROW 变量的字段指定值,请使用限定符表示法 variableName.fieldName ,其后跟随表达式,如以下示例中所示:
CREATE ROW TYPE rectangle_t (start point_t, length real, width real);
DEFINE r rectangle_t; -- Define a variable of a named ROW type
LET r.length = 45.5; -- Assign a value to a field of the variable
当您给 ROW 变量指定值时,可以使用任何有效的表达式。
声明 Opaque 类型变量
Opaque 类型变量保留从不透明数据类型中检索出的数据,这些不透明数据类型是用 CREATE OPAQUE TYPE 语句创建的。不透明类型变量只能保留定义它所依据的同一不透明类型的数据。以下示例定义 opaque 类型的变量 point,它保留二维点的 x 和 y 坐标:
DEFINE b point;
声明变量 LIKE 列
如果使用 LIKE 子句,则数据库服务器指定变量的数据类型和表、同义词或视图中指定列的数据类型相同。
定义为数据库列的变量的数据类型在运行时解析,因此在编译时不需要存在列和表。
可以使用 LIKE 关键字声明某个变量类似于 serial 列,它声明:
- 如果列是 SERIAL 数据类型,就声明一个 INTEGER 变量
- 如果列是 SERIAL8 数据类型,就声明一个 INT8 变量
- 如果列是 BIGSERIAL 数据类型,就声明一个 BIGINT 变量
例如,如果 mytab 表中的 serialcol 列有 SERIAL 数据类型,则可以创建以下的 SPL 函数:
CREATE FUNCTION func1()
DEFINE local_var LIKE mytab.serialcol;
RETURN;
END FUNCTION;
变量 local_var 被视为 INTEGER 变量。
使用逻辑字符语义定义变量
当为当前会话指定了 SQL_LOGICAL_CHAR 配置参数,设置为 'ON'或比 1 大的值时, GBase 8s 在以下数据类型的 SPL 变量的声明中将大小声明解释为逻辑字符,而不是字节:
- CHAR 或 CHARACTER
- CHARACTER VARYING 或 VARCHAR
- LVARCHAR
- NCHAR
- NVARCHAR
- 基于内置字符数据类型的 DISTINCT 类型
- 基于之前列出的数据类型的 DISTINCT 类型
- 任何先前列出的数据类型的 ROW 数据类型字段
- LIST 、MULTISET 或 SET 集合数据类型中的数据类型的元素
为数据库语言环境启用逻辑字符语义可确保有足够的存储空间可供数据类型存储指定数量的逻辑字符。SPL 变量的结构字节大小是数据类型的声明大小乘以 SQL_LOGICAL_CHAR 值,如果为 2 、3 或 4 或者(如果 SQL_LOGICAL_CHAR 设置为 'ON')乘以数字的乘积的数据库语言环境的代码集中最大的逻辑字符所需的存储字节数。
如果客户端会话连接到在创建数据库时启用了 SQL_LOGICAL_CHAR 配置参数的数据库,则该设置将在连接时生效。
在数据类型声明中使用 LIKE 关键字的 DEFINE 语句创建其数据类型与 LIKE 规范引用的列的模式匹配的 SPL 变量。如果已定义,SQL_LOGICAL_CHAR 设置对 DEFINE 使用 LIKE 关键字声明的变量的内存大小没有影响。
有关使用多字节代码集(如 UTF-8)的语言环境中 SQL_LOGICAL_CHAR 设置的影响的详细信息,其中单个逻辑字符可能需要多个字节的存储,请参阅 GBase 8s 管理员参考手册中有关 SQL_LOGICAL_CHAR 配置参数的描述。有关多字节语言环境和逻辑字符的其它信息,请参阅 GBase 8s GLS 用户指南。
声明变量为 PROCEDURE 类型
PROCEDURE 关键字表示在当前作用域中,变量是对 UDR 的调用。
DEFINE 语句不支持 FUNCTION 关键字。不管您在调用用户定义的过程还有用户定义的函数,都使用 PROCEDURE 关键字。
将变量声明为 PROCEDURE 类型表示在当前语句作用域中,该变量不是对内置函数的调用。例如,以下语句定义 length 作为 SPL 例程,而不是作为内置 LENGTH 函数:
DEFINE length PROCEDURE;
...
LET x = length (a,b,c)
此定义在语句块的作用域中禁用内置 LENGTH 函数。如果您已使用名称 length 创建了用户定义的例程,则将使用这样的定义。
如果您使用与聚集函数(SUM 、MAX 、MIN 、AVG 、COUNT)相同的名称或使用名称 extend 创建 SPL 例程,则必须将例程名称限制为所有者名称。
为 BYTE 和 TEXT 数据声明变量
REFERENCES 关键字表示该变量不包含 BYTE 或 TEXT 值,但是是指向 BYTE 或 TEXT 值的指针。可以如同它保存了该值一样使用此变量。
以下示例定义本地 BYTE 变量:
CREATE PROCEDURE use_byte()
DEFINE i INT;
DEFINE l_byte REFERENCES BYTE;
END PROCEDURE --use_byte
如果将 BYTE 或 TEXT 数据类型的变量传递给 SPL 例程,则该数据会传递到数据库服务器并存储在根数据库空间或者 DBSPACETEMP 环境变量指定的数据库空间(如果已设置)。您不需要指定保留数据的文件的位置或名称。仅需要 BYTE 或 TEXT 变量在例程中被定义时的名称。
EXIT
EXIT 语句可以终止 FOR 、FOREACH 、LOOP 或 WHILE 语句。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
condition | 当此计算为 TRUE 时循环终止 | 如果 condition 计算为 FALSE,则循环继续。 | 条件 |
label | 要退出的循环的标签 | 必须是包含 EXIT 语句的循环语句的标签 | 标识符 |
用法
EXIT 语句从迭代传输执行的控制,使封闭语句类型(FOR 、FOREACH 、LOOP 或 WHILE)的最内层循环终止。如果未指定循环标签或 WHEN 添加,则在当前 FOR 、FOREACH 、LOOP 或 WHILE 语句之后的第一个语句处恢复执行。
从 FOREACH 语句 EXIT
如果 EXIT 语句将 FOREACH 语句作为其最内层的结束语句,则 FOREACH 关键字必须跟在 EXIT 关键字之后。如果 FOREACH 语句没有以 EXIT FOREACH 语句结束,则 EXIT FOREACH 语句会无条件地终止 FOREACH 语句。
以下程序段包含 EXIT FOREACH 语句:
FOREACH cursor1 FOR
SELECT * INTO a FROM TABLE(b);
IF a = 4 THEN
DELETE FROM TABLE(b)
WHERE CURRENT OF cursor1;4
EXIT FOREACH;
END IF;
END FOREACH;
从 FOR 、LOOP 或 WHILE 循环 EXIT
如果 EXIT 语句在 FOREACH 语句外发出,则它返回一个错误,除非它是从作为其最内层的 FOR 、FOR LOOP 、LOOP 、WHILE LOOP 或 WHILE 语句发出。在不包含 LOOP 关键字的 FOR 或 WHILE 语句中,EXIT 关键字之后需要相应的 FOR 或 WHILE 关键字。执行从发出 EXIT 语句的最内层循环之后的第一个可执行语句处恢复。
当 EXIT 语句从 FOR LOOP 、LOOP 或 WHILE LOOP 语句发出时,不需要其它关键字,带有或不带有循环标签,但如果 EXIT 关键字之后包含 FOR 、LOOP 或 WHILE 关键字,则该关键字必须对应于从其发出 EXIT 语句的循环的类型。
如果 EXIT 关键字后面跟着循环标签的标识符,并且没有指定 condition,那么在指定了标签的 FOR 、FOR LOOP 、LOOP 、WHILE LOOP 或 WHILE 语句之后的第一个可执行语句将继续执行,这使得 EXIT 语句能够从嵌套循环中退出(如果标记了外层循环)。
如果 WHEN condition 后跟着 EXIT 或 EXIT label 规范,则 EXIT 不会生效直到 condition 为真。如果条件为真,则如果没有指定标签,则在已标记循环之后或最内循环之后继续执行。
如果数据库服务器没有找到指定循环或循环标签,则 EXIT 语句失败。如果从 FOR 、FOREACH 、LOOP 或 WHILE 语句外发出 EXIT ,它将产生错误。
以下示例使用 EXIT FOR 语句。在 FOR 循环中,当 j 变为 6 时, WHILE 循环中的 IF 条件 i = 5 为真。FOR 循环停止执行,SPL 过程继续 FOR 循环之外的下一个语句(在这种情况下,是 END PROCEDURE 语句)。在该示例中,当 j 等于 6 时,该过程结束:
CREATE PROCEDURE ex_cont_ex()
DEFINE i,s,j, INT;
FOR j = 1 TO 20
IF j > 10 THEN
CONTINUE FOR;
END IF
LET i,s = j,0;
WHILE i > 0
LET i = i -1;
IF i = 5 THEN
EXIT FOR;
END IF
END WHILE
END FOR
END PROCEDURE;
以下程序片段显示了在标记的 WHILE LOOP 语句中的两个条件 EXIT 语句,它们嵌套在另一个标记为 LOOP 的语句中:
<<outer>>
LOOP
LET x = x+1;
<<inner>>
WHILE ( i >10 ) LOOP
LET x = x+1;
EXIT inner WHEN x = 2;
EXIT outer WHEN x > 3;
END LOOP inner;
LET x = x+1;
END LOOP outer;
当 x=2 条件为真时,EXIT inner 语句将控制传递给 inner 标签的循环后的 LET 语句。当 x>3 条件为真时,EXIT outer 语句终止 outer 循环的执行。
相关语句
<< Label >> 语句 、FOR 、FOREACH 、LOOP 、WHILE
FOR
当您希望保证循环的终止时,请使用 FOR 语句来初始化受控的(限定的)循环。FOR 语句使用表达式或范围运算符来为循环指定有限数量的迭代。
语法
Range
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
expression | 与 loop_var 相比较的值 | 必须与 loop_var 数据类型相匹配 | 表达式 |
increment_expr | loop_var 增加的正值或负值。缺省为 1(如果 left_expr < right_expr),或者 -1 (如果 left_expr > right_expr) | 必须返回整数。不能返回 0。 | 表达式 |
label | 此循环的循环标签的名称 | 必须存在且在 SPL 例程中的标签名中必须是唯一的 | 标识符 |
left_expr | 开始范围的表达式 | 值必须与 loop_var 的 SMALLINT 或 INT 数据类型相匹配,但是 left_expr 不能等于 right_expr | 表达式 |
loop_var | 确定指定循环次数的变量 | 必须在该语句块的作用域内定义 | 标识符 |
right_expr | 范围中的结束表达式 | 同 left_expr | 表达式 |
用法
数据库服务器在 FOR 语句执行之前计算所有表达式的值。如果其中一个或多个表达式是其值在循环期间更改的变量,则该更改对循环的迭代没有影响。
您可以将来自 SELECT 语句的输出用作表达式。
当 loop_var 等于表达式列表或连接的范围中的每个元素的值,或当它遇到 EXIT FOR 语句时,FOR 循环终止。但如果在 FOR 语句的主体中的赋值尝试修改 loop_var 值,会发生错误。
相对于 left_expr 的 right_expr 大小确定该范围是否按照正增量或负增量单步遍历:
- 如果 left_expr < right_expr ,则增量为正。
- 如果 left_expr > right_expr ,则增量为负。
如果未指定 increment_expr,则每一步的缺省大小为 1,由以上的规则决定是正或负。
使用 TO 关键字定义范围
TO 关键字表示范围运算符。范围由 left_expression 和 right_expression 定义,并且 STEP increment_expr 选项隐式地设置增量数。如果使用 TO 关键字,loop_var 必须是 INT 或 SMALLINT 数据类型。
以下示例显示了两个等同的 FOR 语句。每个语句使用 TO 关键字定义范围。第一个语句使用 IN 关键字,第二个语句使用等号( = )。每个语句使循环执行五次:
FOR index_var IN (12 TO 21 STEP 2)
-- statement block
END FOR;
FOR index_var = 12 TO 21 STEP 2
-- statement block
END FOR;
如果省略 STEP 选项,则数据库服务器给予 increment_expr 的值为 -1 (如果 right_expression 小于 left_expression),或 +1 (如果 right_expression 大于 left_expression)。如果指定了 increment_expr ,则它必须为负数(如果 right_expression 小于 left_expression)或者正数(如果 right expression 大于 left_expression)。
以下示例中的两个语句是等价的。在第一个语句中,STEP 增量是显式的。在第二个语句中,STEP 增量隐式地为 1:
FOR index IN (12 TO 21 STEP 1)
-- statement block
END FOR;
FOR index = 12 TO 21
-- statement block
END FOR;
数据库服务器将 loop_var 的值参数为 left_expression 的值。在后续迭代中,服务器将 increment_expr 添加到 loop_var 的值并检查 increment_expr 以确定 loop_var 值是否仍在 left_expression 和 right_expression 之间。如果这样,会发生下一迭代。否则,会发生循环退出。或者,如果指定另一范围,变量会具有下一范围中的第一个元素的值。
在单个 FOR 语句中指定两个或更多的范围
以下示例显示向前或向后遍历循环并在每一方向使用不同增量值的语句:
FOR index_var IN (15 to 21 STEP 2, 21 to 15 STEP -3)
-- statement body
END FOR;
将表达式列表作为范围使用
数据库服务器将 loop_var 的值初始化为指定的第一个表达式的值。在后续的迭代中,loop_var 值取决于下一个表达式的值。当数据库服务器已计算了列表中的最后一个表达式的值并使用了它时,循环停止。
IN 列表中的表达式无须是数值,只要不在 IN 列表中使用范围运算符。以下示例使用字符表达式列表:
FOR c IN ('hello', (SELECT name FROM t), 'world', v1, v2)
INSERT INTO t VALUES (c);
END FOR;
以下 FOR 语句显示了数值表达式列表的使用:
FOR index IN (15,16,17,18,19,20,21)
-- statement block
END FOR;
在同一 FOR 语句中混合范围和表达式列表
如果 loop_var 是 INT 或 SMALLINT 值,您可以在同一 FOR 语句中混合范围和表达式列表。以下示例显示使用整数变量的混合。表达式列表中的值包括从 SELECT 语句返回的值、整数变量和常量的和、从名为 p_get_int 的 SPL 函数返回的值和整数变量:
CREATE PROCEDURE for_ex ()
DEFINE i, j INT;
LET j = 10;
FOR i IN (1 TO 20, (SELECT c1 FROM tab WHERE id = 1),
j+20 to j-20, p_get_int(99),98,90 to 80 step -2)
INSERT INTO tab VALUES (i);
END FOR;
END PROCEDURE;
指定已标记的FOR 循环
要创建已标记的 FOR 循环,在最初的 FOR 关键字之前声明循环标签,在 END FOR 关键字之后重复此标签,如下所示:
CREATE PROCEDURE ex_cont_ex()
DEFINE i,s,j, INT;
<<for_lab>>
FOR j = 1 TO 20
IF j > 10 THEN
CONTINUE FOR;
END IF
LET i,s = j,0;
WHILE i > 0
LET i = i -1;
IF i = 5 THEN
EXIT for_lab;
END IF
END WHILE
END FOR for_lab
END PROCEDURE;
这里 EXIT for_lab 语句具有与 EXIT 或 EXIT FOR 关键字相同的作用,都终止了 FOR 循环和例程。在此示例中,包含 EXIT for_lab 语句的语句具有与 EXIT for_lab WHEN i = 5 相同的效果。
您还可以标记紧跟在初始 FOR 关键字之前的循环 <
您可以使用类似的语法创建一个未标记的循环,省略紧跟在初始 FOR 关键字之前的 <
相关语句
<< Label >> 语句 、CONTINUE 、EXIT 、LOOP 、FOREACH 、WHILE
FOREACH
使用 FOREACH 语句声明一个直接游标,该游标可以查询和操纵查询返回的结果集的多个的行或者来自集合的多个元素。
语法
SPL 的 FOREACH 语句可以创建的直接顺序游标与 SQL 的 DECLARE 语句可以在 SPL 例程中创建的动态游标不同。(有关 SPL 例程中动态游标的语法和用法,请参阅在 SPL 例程中声明动态游标。)
例程调用
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
cursor | 作为直接游标的名称而声明的标识符 | 在例程的游标名称、准备好的语句以及 SPL 变量中必须是唯一的 | 标识符 |
data_var | 接收返回值的调用例程中的 SPL 变量 | data_var 的数据类型必须与返回的值相对应 | 标识符 |
function, procedure | 要执行的 SPL 函数或过程 | 函数或过程必须存在 | 数据库对象名 |
SPL_var | 包含要执行例程的名称的 SPL 变量 | 必须是 CHAR 、VARCHAR 、NCHAR 或 NVARCHAR 类型 | 标识符 |
用法
要执行 FOREACH 语句,数据库服务器应采用以下操作:
- 它声明并隐式地打开游标。
- 它从包含在 FOREACH 循环中的查询获取第一行,否则从调用的例程获取第一组值。
- 它对变量列表中的每个变量赋值,所赋的值是来自 SELECT 语句或调用的例程创建的活动集合的相应值。
- 它执行语句块。
- 它从 SELECT 语句获取下一行或对每个迭代调用例程,并且重复步骤 3 和 4。
- 当它发现没有其它行满足 SELECT 语句或调用的例程时,它会终止循环。当循环终止时,它关闭隐式游标。
因为语句块包含其它 FOREACH 语句,所以可以嵌套游标。对于嵌套的游标数量没有限制。
返回多个行、集合元素或值集合的 SPL 例程被称为游标函数。只返回一行或一个值的 SPL 例程是非游标函数。
该 SPL 过程使用 SELECT ... INTO 子句、显式地已命名游标和过程调用来说明 FOREACH 语句:
CREATE PROCEDURE foreach_ex()
DEFINE i, j INT;
FOREACH SELECT c1 INTO i FROM tab ORDER BY 1
INSERT INTO tab2 VALUES (i);
END FOREACH
FOREACH cur1 FOR SELECT c2, c3 INTO i, j FROM tab
IF j > 100 THEN
DELETE FROM tab WHERE CURRENT OF cur1;
CONTINUE FOREACH;
END IF
UPDATE tab SET c2 = c2 + 10 WHERE CURRENT OF cur1;
END FOREACH
FOREACH EXECUTE PROCEDURE bar(10,20) INTO i
INSERT INTO tab2 VALUES (i);
END FOREACH
END PROCEDURE; -- foreach_ex
当出现以下任意情形时,会关闭选择游标:
- 游标不再返回行。
- 游标是一个没有 HOLD 指定选择游标,并且使用 COMMIT 或 ROLLBACK 语句完成某项事务。
- 执行 EXIT 语句,它转移对 FOREACH 语句的控制。
- 在 FOREACH 语句的主体之内发生未俘获的异常。(请参阅 ON EXCEPTION。)
- 正执行此游标例程的调用例程中的游标(在 FOREACH 循环中)出于任何原因而关闭。
FOREACH 语句不能定义 SCROLL 游标。每个 FOREACH 游标都是一个顺序游标,它只能获取来自活动集顺序中的下一行。FOREACH 定义的游标每次打开时只能读取活动集。
使用 SELECT ... INTO 语句
如在 FOREACH 的图表中所指出的,并非 SELECT 语句的所有子句和选项都可用在 FOREACH 语句中。FOREACH 语句中的 SELECT 语句必须包含 INTO 子句。它还可以包括 UNION 和 ORDER BY 子句,但不能使用 INTO TEMP 子句。关于 SELECT 语法和用法的完整描述,请参阅 SELECT 语句。变量列表中的每个变量的数据类型和计数必须与 SELECT ... INTO 语句返回的每个值相匹配。
如果您在 FOREACH 语句中包含分号(; )以终止 SELECT ... INTO 规范,则该语句发出错误。例如,以下示例程序片段因产生语法错误而失败:
CREATE DBA PROCEDURE IF NOT EXISTS shapes()
DEFINE vertexes SET( point NOT NULL );
DEFINE pnt point;
SELECT definition INTO vertexes FROM polygons
WHERE id = 207;
FOREACH cursor1 FOR
SELECT * INTO pnt FROM TABLE(vertexes); -- Semicolon not valid
. . .
END FOREACH
. . .
END PROCEDURE;
在上面的示例中,您可以通过删除紧跟在 TABLE(vertexes) 规范之后的分号来避免此错误。
使用 SELECT 语句的 ORDER BY 子句
使用 SELECT 语句的 ORDER BY 子句表示查询返回多行。除非您使用 SQL 的 DECLARE 语句定义 Select 游标或 Function 游标,否则如果您在 FOREACH 循环的上下文之外指定 ORDER BY 子句以在 SPL 例程中单独处理返回的行,数据库服务器将发出错误。
有关 SPL 例程中 DECLARE 语句的语法和用法,请参阅在 SPL 例程中声明动态游标。
使用 Hold 游标
WITH HOLD 关键字指定(通过提交或回滚)关闭事务时游标仍保持打开。
更新或删除由游标名称标识的行
如果打算在 UPDATE 或 DELETE 语句(这些语句在 FOREACH 循环中 cursor 的当前行上操作)中使用 WHERE CURRENT OF cursor 子句,请在 FOREACH 循环中指定 cursor 名称。虽然不能在 FOREACH 语句的 SELECT ... INTO 段中包含 FOR UPDATE 关键字,但该游标的表现类似 FOR UPDATE 游标。
有关锁定的讨论。请参阅使用 Update 游标进行锁定部分。有关隔离级别的讨论,请参阅SET ISOLATION 语句的描述。
使用集合变量
FOREACH 语句允许您为 SPL 集合变量声明游标。 这种游标称为集合游标。使用集合变量访问集合(SET、 MULTISET 、LIST)列的元素。当要访问集合变量中的一个或多个元素时,请使用游标。
以下来自 SPL 例程的摘抄显示了如果填充集合游标然后如何使用游标访问个别元素:
DEFINE a SMALLINT;
DEFINE b SET(SMALLINT NOT NULL);
SELECT numbers INTO b FROM table1 WHERE id = 207;
FOREACH cursor1 FOR
SELECT * INTO a FROM TABLE(b);
...
END FOREACH;
在此示例中,SELECT 语句从集合变量 b 中一次选择一个元素插入到元素变量 a 中。该投影列表是星号,因为集合变量 b 包含内置类型的集合。变量 b 与 TABLE 关键字作为 Collection-Derived Table 一起使用。有关更多信息,请参阅集合派生表。
下一个示例还显示了如果填充集合变量以及如果使用变量存取个别元素。但是,此示例在它的投影列表中使用 ROW 类型字段的列表:
DEFINE employees employee_t;
DEFINE n VARCHAR(30);
DEFINE s INTEGER;
SELECT emp_list into employees FROM dept_table
WHERE dept_no = 1057;
FOREACH cursor1 FOR
SELECT name,salary
INTO n,s FROM TABLE( employees ) AS e;
...
END FOREACH;
这里的集合变量 employees 包含一个 ROW 类型的集合。每个 ROW 类型包含 name 和 salary 字段。该集合查询每次查询一个名字和薪水,并将 name 放到 n 中将 salary 放到 s 中。AS 关键字声明 e 作为集合派生表 employees 的别名。只要 SELECT 语句执行,该别名就存在。
集合游标上的限制
当使用集合游标从集合变量获取个别元素时,FOREACH 语句具有以下限制:
- 它不能包含 WITH HOLD 关键字。
- 它必须在 FOREACH 循环中包含受限制的 SELECT 语句。
此外,与集合游标相关联的 SELECT 语句具有以下限制:
- 它的一般结构为 SELECT… INTO … FROM TABLE。该语句从按照 TABLE 关键字指定的集合变量中一次选择一个元素放入另一个被称为 element variable 的变量。
- 它不能在 Projection 列表中包含表达式。
- 它不能包含以下子句或选项:WHERE 、GROUP BY 、ORDER BY 、HAVING 、INTO TEMP 和 WITH REOPTIMIZATION。
- 该元素变量的数据类型必须与集合的元素类型相同。
- 元素变量的数据类型可以是任何 Opaque 、Distinct 或集合数据类型,或者任何除 BIGSERIAL 、BLOB 、BYTE 、CLOB 、SERIAL 、SERIAL8 或 TEXT 外的内置数据类型。
- 如果集合变量包含 Opaque 类型、Distinct 类型、内置类型或集合数据类型,则投影列表必须是星号(*)。
- 如果集合包含 ROW 类型,则投影列表可以是一个或多个字段名称的列表。
修改集合变量中的元素
要修改 SPL 例程中的集合的元素,必须首先使用 FOREACH 语句声明游标。
然后在 FOREACH 循环内,将集合变量用作 SELECT 查询中的集合派生的表,从集合变量一次选择一个元素。
当游标放置到要更新的元素上时,可以如下使用 WHERE CURRENT OF 子句:
- 带有 WHERE CURRENT OF 子句的 UPDATE 语句更新集合变量的当前元素中的值。
- 带有 WHERE CURRENT OF 子句的 DELETE 语句删除集合变量的当前元素中的值。
同 FOREACH 一起使用 Select 游标
当使用 FOREACH 语句时,如果来自查询的结果集要被更改,则不要使用此结果集作为 FOREACH 循环的退出条件。例如,如果 FOREACH 语句声明了一个期望返回 30 行的 Select 游标,但是 FOREACH 循环中的 DELETE 、INSERT 或 UPDATE 操作修改了此查询的结果集,这可能导致意外行为。要确保 FOREACH 循环按照预期工作,请确保 FOREACH 语句中的任何 Select 游标在您开始修改结果集之前完成执行。
避免从对查询结果返回的行执行 DML 操作的 FOREACH 循环产生意外结果的一种方法是在 SELECT 语句中使用 ORDER BY 子句来实现结果集。
在 FOREACH 循环中调用 UDR
通常,使用以下准则用于从 SPL 例程调用另一个 UDR:
- 要调用用户定义的过程,请使用 EXECUTE PROCEDURE procedure name。
- 要调用用户定义的函数,请使用 EXECUTE FUNCTION function name (如果用户定义的函数是用 CREATE PROCEDURE 语句创建的,则使用 EXECUTE PROCEDURE function name )。
如果使用 EXECUTE PROCEDURE,则数据库服务器首先寻找具有您指定名称的用户定义的过程。如果它发现该过程,则数据库服务器执行它。如果它找不到该过程,则它寻找具有相同名称的用户定义的函数来执行。如果数据库服务器既找不到函数也找不到过程,则发出错误消息。如果使用 EXECUTE FUNCTION ,则数据库服务器寻找具有您指定名称的用户定义的函数。如果找不到该名称的函数,则数据库服务器发出错误消息。
SPL 函数可以返回零(0)个或多个值或行。
变量列表中的每个变量的数据类型和计数必须与函数返回的每个值相匹配。
相关的语句
CONTINUE 、EXIT 、FOR 、LOOP 、WHILE
GOTO
使用 GOTO 语句将程序执行的控制转移到具有指定语句标签的语句。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
label | 此循环的循环标签的名称 | 在此 SPL 例程的标签中必须是唯一的 | 标识符 |
用法
GOTO 语句无条件地分支到语句标签。语句标签在其业务范围内必须是唯一的,并且必须在可执行语句之前。成功执行后,GOTO 语句将控制转移到标记的语句或语句块。
以下程序片段中,如果 j 变量的值大于 100 ,则 jump_back 函数将控制传递给有语句标签 back 的 LET 语句。
CREATE FUNCTION jump_back()
RETURNING INT;
DEFINE i,j INT;
...
<<back>>
LET j = j + i
FOR i IN (1 TO 52 STEP 5)
IF i < 11 THEN
LET j = j + 3
CONTINUE FOR;
END IF;
IF j > 100 THEN
GOTO back
END IF;
RETURN j WITH RESUME;
END FOR;
END FUNCTION;
GOTO 语句在 ON EXCEPTION 语句块中无效。
GOTO 语句引用的语句标签的标识符必须在数据库中存在,并在此 SPL 例程的语句标签和循环标签中必须是唯一的,而且必须在 GOTO 语句可以到达的作用域内。
相关语句
<< Label >> 语句
IF
使用 IF 语句在 SPL 例程中创建逻辑分支。
语法
用法
数据库服务器按下列步骤处理 IF 语句:
- 如果 IF 关键字之后的条件为真,则执行 IF 语句的第一个 THEN 关键字之后的任何语句,并且 IF 语句终止。
- 如果初始 IF 语句条件的结果为假,当存在 ELIF 子句,则数据库服务器将计算 ELIF 关键字之后的条件。
- 如果 ELIF 条件的结果为真,则执行 ELIF 语句的 THEN 关键字后的任何语句,并且 IF 语句终止。
- 如果第一个 ELIF 子句中的条件的结果也为假,则数据库服务器会计算下一个 ELIF 子句的条件,如果它为真,则继续执行上一个步骤。如果为假,数据库服务器将计算连续 ELIF 子句中的条件,直到找到一个条件为真,在这种情况下,它会执行该 ELIF 子句中的 THEN 关键字后面的语句列表,然后 IF 语句终止。
- 如果 IF 语句中没有条件为真,但存在 ELSE 子句,则执行 ELSE 关键字之后的语句,并且 IF 语句终止。
- 如果 IF 语句中的条件都不为真,且不存在 ELSE 子句,则 IF 语句终止,而不执行任何语句列表。
ELIF 子句
使用 ELIF 子句指定一个或多个附加条件以计算值。如果 IF 条件为假,则计算 ELIF 条件的值。如果 ELIF 条件为真,则执行 ELIF 子句中跟随 THEN 关键字之后的子句。
如果当 ELIF 条件为真时 ELIF 子句的 THEN 关键字之后没有语句,则程序将控制从 IF 语句传递给下一条语句。
ELSE 子句
如果没有为真的上一级统计存在于 IF 子句或任何 ELIF 子句,则执行 ELSE子句。
在以下示例中,SPL 函数使用具有 ELIF 子句和 ELSE 子句的 IF 语句。IF 语句比较两个字符串。
函数显示 1 来指示第一个字符串按字母顺序出现在第二个字符串前面,或则当前第一个字符串按字母顺序出现在第二个字符串后面时,则显示 -1 。如果字符串都相同,则返回量(0)。
CREATE FUNCTION str_compare (str1 CHAR(20), str2 CHAR(20))
RETURNING INT;
DEFINE result INT;
IF str1 > str2 THEN LET result =1;
ELIF str2 > str1 THEN LET result = -1;
ELSE LET result = 0;
END IF
RETURN result;
END FUNCTION -- str_compare
IF 语句中的条件
如同在 WHILE 语句中,如果 condition 中的任何表达式求值为 NULL,则条件不能为真,除非您正在使用 IS NULL 运算符对 NULL 显式地进行测试。以下规则总结了条件中的 NULL 值:
- 如果表达式 x 求值为 NULL,则按照定义,x 不为真。而且,NOT (x) 也不为真。
- IS NULL 是可使 x 恢复为真的唯一运算符。即 x IS NULL 为真,x IS NOT NULL 不为真。
如果条件中的表达式含有来自未初始化的 SPL 变量的 UNKNOWN 值,则语句终止并出现异常。
可以仅在触发器例程中的 IF 语句中将触发器类型 Boolean 运算符(DELETING 、INSERTING 、SELECTING 或 UPDATING)作为条件。
IF 语句列表
IF 语句列表
IF 语句列表中允许的 SPL 语句的子集
您可以在 IF 语句列表中使用以下任何 SPL 语句:
- <<Label >>
- CALL
- CASE
- CONTINUE
- EXIT
- FOR
- FOREACH
- GOTO
- IF
- LET
- LOOP
- RAISE EXCEPTION
- RETURN
- SYSTEM
- TRACE
- WHILE
IF 语句列表 的“SPL 语句的子集”语法图请参阅以上列出的 SPL 语句。
IF 语句中无效的 SQL 语句
在 IF 语句列表的语法图中的“SQL 语句的子集”元素引用了所有 SQL 语句,除以下 SQL 语句,这些语句在 IF 语句列表中无效:
- ALLOCATE DESCRIPTOR
- CLOSE DATABASE
- CONNECT
- CREATE DATABASE
- CREATE PROCEDURE
- DATABASE
- DEALLOCATE DESCRIPTOR
- DESCRIBE
- DISCONNECT
- EXECUTE
- FLUSH
- GET DESCRIPTOR
- GET DIAGNOSTICS
- INFO
- LOAD
- OUTPUT
- PUT
- SET AUTOFREE
- SET CONNECTION
- SET DESCRIPTOR
- UNLOAD
- WHENEVER
仅当使用 INTO TEMP 子句将 SELECT 语句的结果集存储在临时表中时,您可以使用 SELECT 语句。
相关语句
WHILE
LET
使用 LET 语句对变量赋值或调用用户定义的 SPL 例程,并指定返回值或对 SPL 变量赋值。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
function | 要调用的 SPL 函数 | 必须存在于数据库中 | 标识符 |
SPL_var | 接收函数、表达式或查询返回的值的 SPL 变量 | 必须被定义并在语句块的作用域中 | 标识符; |
用法
LET 语句可将由表达式、函数或查询返回的值指定给 SPL 变量。在运行时,首先计算要被指定的值。如果可能,得到的值被强制转型为 SPL_var 数据类型并且进行赋值。如果不可能转换,则出现错误,并且变量的值保持为未定义状态。(将单一值指定给单一 SPL 变量的 LET 操作称为简单赋值。)
复合赋值给多个 SPL 变量指定多个表达式。表达式列表中的表达式的数据类型不需要与变量列表中的对应变量的数据类型相匹配。因为数据库服务器可自动转换数据类型。(对于强制转型的详细讨论,请参阅 《GBase 8s SQL 指南:参考》。)
在多赋值操作中,等号(=)左边的变量数必须与等号(=)右边列出的由函数、表达式和查询返回的值的数量相匹配。以下示例显示了对 SPL 变量赋值的几个 LET 语句:
LET a = c + d ;
LET a,b = c,d ;
LET expire_dt = end_dt + 7 UNITS DAY;
LET name = 'Brunhilda';
LET sname = DBSERVERNAME;
LET this_day = TODAY;
不能使用等号(=)右边的多个值对其它值进行操作。例如,以下语句是无效的:
LET a,b = (c,d) + (10,15); -- INVALID EXPRESSION
在 LET 语句中使用 SELECT 语句
本部分中的示例在 LET 语句中使用 SELECT 语句。您可以使用 SELECT 语句对等号(=)运算符左边的一个或多个变量赋值,如以下示例中所示:
LET a,b = (SELECT c1,c2 FROM t WHERE id = 1);
LET a,b,c = (SELECT c1,c2 FROM t WHERE id = 1), 15;
您不能使用 SELECT 语句使多个值对其它值进行运算。以下示例是无效的:
LET a,b = (SELECT c1,c2 FROM t) + (10,15); -- INVALID CODE
因为 LET 语句等价于 SELECT ... INTO 语句,所以以下示例中的两个语句有相同结果:a=c 和 b=d:
CREATE PROCEDURE proof()
DEFINE a, b, c, d INT;
LET a,b = (SELECT c1,c2 FROM t WHERE id = 1);
SELECT c1, c2 INTO c, d FROM t WHERE id = 1
END PROCEDURE
如果 SELECT 语句返回多行,您必须将 SELECT 语句包括在 FOREACH 循环中。
有关 SELECT 语法和用法的描述,请参阅 SELECT 语句。
在 LET 语句中调用函数
您可以在 LET 语句中调用用户定义的函数,并将返回值指定给接收函数返回的值的 SPL 变量。
SPL 函数可将多个值(即,来自同一行中多个列的值)返回到变量名称列表中。换句话说,该函数可在其 RETURN 语句中有多个值,并且 LET 语句可有多个变量以接收返回值。
当调用函数时,必须对函数指定所有必要的参数,除非函数的参数有缺省值。如果您使用语法(如 name = 'smith')给被调用的函数中的多个参数中的一个指定了名称,则必须命名所有参数。
选择和返回多个行的 SPL 函数必须包括在 FOREACH 循环中。
以下两个示例显示了有效的 LET 语句:
LET a, b, c = func1(name = 'grok', age = 17);
LET a, b, c = 7, func2('orange', 'green');
以下 LET 语句无效,因为它试图添加两个函数的输出,然后将总和指定给两个变量 a 和 b。
LET a, b = func1() + func2(); -- 无效代码
您可以轻松地将此 LET 语句分为两个有效的 LET 语句:
LET a = (func1() + func2());
LET b = a; -- 有效代码
在 LET 语句中调用的函数可以有 COLLECTION 、SET 、MULTISET 或 LIST 参数。您可以将函数返回的值指定给变量,例如:
LET d = function1(collection1);
LET a = function2(set1);
在第一个语句中,SPL 函数 function1 接受 collection1 (即,任何集合数据类型)作为参数并将其值返回给变量 d。第二个语句中,SPL 函数 function2 接受s set1 作为参数并将值返回给变量 a。
LOOP
使用 LOOP 语句定义具有不确定迭代次数的循环。
语法
Range
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
expression | 与 loop_var 相比的值 | 必须与 loop_var 数据类型相匹配 | 表达式 |
increment_expr | loop_var 增加的正或负值 | 必须返回整数。不能返回 0。 | 表达式 |
label | 此循环的循环标签的名称 | 在此 SPL 例程的标签中必须是唯一的 | 标识符 |
left_expression | 范围开始的表达式 | 值必须符合 loop_var 的 SMALLINT 或 INT 数据类型 | 表达式 |
loop_var | 确定循环执行的次数的变量 | 必须已定义并且在此语句块中的作用域中 | 标识符 |
right_expression | 范围的结束表达式 | 同 left_expression | 表达式 |
用法
LOOP 语句是类似于 FOR 和 WHILE 语句的迭代语句。就像 FOR 和 WHILE,LOOP 语句具有可选的循环标签。它可以包含 CONTINUE 语句以指定另一个迭代,并且 EXIT 语句会终止循环的执行。
除了在其功能中类似于 FOR 和 WHILE 之外,LOOP 语句可以使用语句块之前的 FOR 或 WHILE 语法。下面的部分描述了 LOOP 语句的几种形式,包括:
- 无限期迭代语句循环的简单 LOOP 语句
- FOR LOOP 语句,使用 FOR 语句语法指定有限数量的迭代
- WHILE LOOP 语句,在指定条件为真时迭代
- 每个 LOOP 语句的标签版本,可以终止深层嵌套循环。
简单 LOOP 语句
以下程序片段说明了 LOOP 语句的样本:
LOOP
LET i = i + 1;
IF i = 5 THEN EXIT;
ELSE
CONTINUE;
END IF
END LOOP;
在此示例中,IF 语句限制了迭代的次数。这里 CONTINUE 和 EXIT 语句省略了可选的 LOOP 关键字,但是在语句循环的结束时需要 END LOOP 语句。类似的 FOR 或 WHILE 关键字在 CONTINUE 和 EXIT 语句中分别需要 FOR 或 WHILE 关键字。
下一个示例使用条件 EXIT 语句终止循环:
LOOP
LET i = i + 1;
EXIT WHEN i = 4;
END LOOP;
在 EXIT 语句之后不需要标识循环语句类型的关键字,如 FOR 、WHILE 或 FOREACH 语句中的 EXIT 语句的情况。当条件 i = 4 为真时,程序控制从 LOOP 语句传递到 END LOOP 关键字之后的任何语句。
FOR LOOP 语句
FOR LOOP 语句使用 FOR 语句语法指定变量和变量可以采用的值的范围。循环迭代,直到达到对这些值的指定限制,或者直到控制被传递到循环之外,如以下示例中的无条件 EXIT 语句所示:
FOR i IN (1 TO 5) LOOP
IF i = 5 THEN EXIT;
ELSE
CONTINUE;
END LOOP;
在此 FOR LOOP 语句中,FOR 关键字可以后跟 EXIT 或 CONTINUE 关键字 ,但是不需要 FOR 关键字,因为它在一个普通的 FOR 语句中。
以下示例用同等功能的条件 EXIT 语句替换 IF 语句:
FOR i IN (1 TO 5) LOOP
EXIT WHEN i = 5;
END LOOP;
WHILE LOOP 语句
要创建 WHILE LOOP 语句,循环,可以立即使用 LOOP 语句跟随 WHILE condition 规范。在条件变为假之后,或者当一些其他语句从循环中传递程序控制时,结果循环终止。在以下 WHILE LOOP 语句中,条件指定循环在循环变量 i 增加到值 6 之后终止:
WHILE (i < 6) LOOP
LET i = i + 1;
IF i = 5 THEN EXIT;
ELSE
CONTINUE;
END IF
END LOOP;
与在 FOR LOOP 语句中一样,EXIT 和 CONTINUE 关键字不需要指定循环语句类型,但是如果 XIT WHILE 和 CONTINUE WHILE 替换了 EXIT 和 CONTINUE 关键字,则该示例不会受到影响。但是,END LOOP 关键字是必需的,因为 GBase 8s 将 WHILE LOOP (和 FOR LOOP)语句视为 LOOP 语句,尽管它们的初始 FOR 和 WHILE 规范。
Labeled LOOP 语句
所有形式的 LOOP 语句,包括 FOR LOOP 、WHILE LOOP 和简单 LOOP 语句可以具有语句标签。您可以按以下步骤创建带标签的 LOOP 语句:
- 写一个有效的 LOOP 、FOR LOOP 或 WHILE LOOP 语句。
- 通过紧接在 LOOP 、FOR LOOP 或 WHILE LOOP 语句的第一行之前的尖括号(<<loop_label>>)之间包含一个 SQL 标识符(它不是同一 SPL 例程中的标签的名称)来创建语句标签。
- 输入相同的 SQL 标识符,但是不带尖括号分隔符,紧接在终止语句的 END LOOP 关键字之后,现在这是一个带标签的循环语句。
标记的 LOOP 语句的一个优点是它们可以在 EXIT 语句中引用。当执行 EXIT label 语句时,程序控制从 EXIT 语句传递到跟在指定循环标记之后的语句。
在以下示例中,标记的 WHILE LOOP 循环(其循环标记标识符是 endo)是标记的 LOOP 语句的语句块的一部分,其循环标签标识符是 voort。如果条件 EXIT 语句 EXIT endo WHEN x = 7: 检测到的条件为真,则程序控制传递到 END LOOP endo 内部生成之后的 LET x = x + 1 语句。如果条件语句 EXIT voort WHEN x > 9 检测到其条件为真,则程序控制传递到 END LOOP voort 语句之后的 LET x = x + 1 语句,并且 x 的值不能被 LET 语句递增。
<<voort>>
LOOP
LET x = x+1;
<<endo>>
WHILE ( i < 10 ) LOOP
LET x = x+1;
EXIT endo WHEN x = 7;
EXIT voort WHEN x > 9;
END LOOP endo;
LET x = x+1;
END LOOP voort;
使用 FOR 语句语法指定变量和变量可以采用的值的范围。循环迭代,直到达到对这些值的指定限制,或者直到控制被传递到循环之外,如以下示例中的无条件 EXIT 语句所示:
FOR i IN (1 TO 5) LOOP
IF i = 5 THEN EXIT;
ELSE
CONTINUE;
END LOOP;
在 FOR LOOP 语句中,FOR 关键字可以后跟 EXIT 或 CONTINUE 关键字,但是不需要 FOR 关键字,因为它在一个普通的 FOR 语句中。
以下示例用同等功能的条件 EXIT 语句替换 IF 语句:
FOR i IN (1 TO 5) LOOP
EXIT WHEN i = 5;
END LOOP;
相关的语句
<< Label >> 语句 、FOR 、WHILE
ON EXCEPTION
使用 ON EXCEPTION 语句指定操作,这些操作是在语句块的执行期间对任何错误或对一个或多个指定错误的列表采取的。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
error_data_var | SPL 变量,用于接收由 SQL 错误或由用户定义的异常返回的字符串 | 必须是字符类型以接收错误消息。必须在当前的语句块中有效。 | 标识符 |
error_number | SQL 错误号或由将被捕获的 RAISE EXCEPTION 语句定义的号码 | 必须是整数类型。在当前的语句块中必须有效。 | 精确数值 |
ISAM_error_var | SPL 变量,接收出现的异常的 ISAM 错误号 | 同 error_number | 标识符 |
SQL_error_var | SPL 变量,接收出现的异常的 SQL 错误号 | 同 ISAM_error_var | 标识符 |
用法
ON EXCEPTION 语句与 RAISE EXCEPTION 语句一起提供 SPL 的错误俘获和错误恢复机制。ON EXCEPTION 可以在 SPL 例程执行时指定您想要捕获的错误,并指定当前语句块中发生错误时要采取的操作。ON EXCEPTION 语句可以指定 IN 子句中的错误号列表,或可以不包括 IN 子句。如果省略 IN 子句,则俘获所有错误。
语句块可以包括多个 ON EXCEPTION 语句。被俘获的异常可以是系统定义的也可以是用户定义的。
ON EXCEPTION 语句的作用域是包含它的语句块,以及嵌套在语句块中的其它语句块,除非其中之一嵌套的语句块提供了覆盖外部语句的 ON EXCEPTION 语句。
当设陷阱捕捉到异常时,会清除错误状态。
如果您指定某个变量来接收 ISAM 错误,但不存在跟随的 ISAM 错误,则将零(0)指定给该变量。如果指定某个变量来接收错误文本,但不存在错误文本,则该变量存储空字符串。
触发器操作中不支持 ON EXCEPTION
当从以下调用上下文中的 SPL 例程发出时,ON EXCEPTION 语句无效:
- 在触发例程中
- 在表的触发器的 Action 子句或 Correlated Action 子句中
- 在视图上的 INSTEAD OF 触发器的 Action 子句中。
当 UDR 在这些上下文中包含 ON EXCEPTION ,数据库服务器忽略 ON EXCEPTION 语句。
ON EXCEPTION 语句的放置
ON EXCEPTION 语句是一个声明性语句,不是可执行语句。出于此原因,ON EXCEPTION 必须在任何可执行语句之前并且在 SPL 中必须跟随任何 DEFINE 语句。
因为 SPL 例程的主体是语句块,所以 ON EXCEPTION 语句进程在例程的开头出现,并应用于例程的整个代码中。
语句示例显示了 ON EXCEPTION 语句的正确放置,以致于 FOREACH 语句在发生错误可以继续处理行。过程 X( ) 从表 A 读取客户编号,并将其插入到表 B。因为 INSERT 语句在 ON EXCEPTION 语句的作用域内,所以 INSERT 操作期间的错误会导致执行控制转移到 FOREACH 游标的下一行,而不用终止此 FOREACH 循环。
CREATE PROCEDURE X()
DEFINE v_cust_num CHAR(20);
FOREACH cs_insert FOR SELECT cust_num INTO v_cust_num FROM A
BEGIN
ON EXCEPTION
END EXCEPTION WITH RESUME;
INSERT INTO B(cust_num) VALUES(v_cust_num);
END
END FOREACH
END PROCEDURE
下一示例中,函数 add_salesperson( ) 向表中插入一组值。如果表不存在,则先创建该表再插入值。该函数还返回表中的总行数:
CREATE FUNCTION add_salesperson(last CHAR(15), first CHAR(15))
RETURNING INT;
DEFINE x INT;
N EXCEPTION IN (-206) -- If no table was found, create one
CREATE TABLE emp_list
(lname CHAR(15),fname CHAR(15), tele CHAR(12));
INSERT INTO emp_list VALUES -- and insert values
(last, first, '800-555-1234');
END EXCEPTION WITH RESUME;
INSERT INTO emp_list VALUES (last, first, '800-555-1234');
SELECT count(*) INTO x FROM emp_list;
RETURN x;
END FUNCTION;
当出现错误时,数据库服务器搜索捕获错误代码的最后一个 ON EXCEPTION 语句。如果数据库服务器找不到相关 ON EXCEPTION 语句,则错误代码传回调用上下文(SPL 例程、应用程序或交互用户),执行终止。
在前面的示例中,在指定错误 -206 的 IN 子句中需要减号(-),大多数错误代码都是负整数。
下一示例使用了两个具有相同错误号的 ON EXCEPTION 语句,因而可以在两个嵌套级别中捕获错误代码 691 。除了标有 { 6 } 的 DELETE 语句,所有其它 DELETE 语句都在第一个 ON EXCEPTION 语句的作用域中。标有 { 1 } 和 { 2 } 的 DELETE 语句都在内部 ON EXCEPTION 语句的作用域中:
CREATE PROCEDURE delete_cust (cnum INT)
ON EXCEPTION IN (-691) -- children exist
BEGIN -- Begin-end so no other DELETEs get caught in here.
ON EXCEPTION IN (-691)
DELETE FROM another_child WHERE num = cnum; { 1 }
DELETE FROM orders WHERE customer_num = cnum; { 2 }
END EXCEPTION -- for error -691
DELETE FROM orders WHERE customer_num = cnum; { 3 }
END
DELETE FROM cust_calls WHERE customer_num = cnum; { 4 }
DELETE FROM customer WHERE customer_num = cnum; { 5 }
END EXCEPTION
DELETE FROM customer WHERE customer_num = cnum; { 6 }
END PROCEDURE
使用 IN 子句捕获特定的异常
如果 SQL 错误代码或 ISAM 错误代码与错误号列表中的异常代码相匹配,则会俘获错误。在错误列表中的搜索重左边开始,并在搜索到第一个匹配时停止。可以使用没有 IN 子句的 ON EXCEPTION 语句和具有 IN 子句的一个或多个 ON EXCEPTION 语句的组合。当出现错误时,数据库服务器搜索捕获特殊错误代码的 ON EXCEPTION 语句的最后一个声明。
CREATE PROCEDURE ex_test()
DEFINE error_num INT;
...
ON EXCEPTION SET error_num
-- action C
END EXCEPTION
ON EXCEPTION IN (-300)
-- action B
END EXCEPTION
ON EXCEPTION IN (-210, -211, -212) SET error_num
-- action A
END EXCEPTION
在上一示例中的语句序列总结为:
- 测试是否有错误。
- 如果出现错误 -210 、-211 或 -212 ,则采取操作 A。
- 如果出现错误 -300 ,则采取操作 B。
- 如果出现任何其它错误,则采取操作 C。
接收 SET 子句中的错误信息
如果使用 SET 子句,但出现异常时,SQL 错误代码和(可选地)ISAM 错误代码被插入到在 SET 子句中指定的变量中,如果提供 error_data_var,则数据库服务器返回的任何错误文本被放入到 error_data_var。错误文本包括类似损坏的表或列名称的信息。
强制例程继续
ON EXCEPTION 语句的放置中的第一个示例包括 WITH RESUME 关键字,用于指定如果 ON EXCEPTION 语句捕获错误,则 FOREACH 循环的执行在 cs_insert 游标的发出错误的行的下一行继续。如果活动集的最后一行发出了错误,则该过程退出。在 X 过程执行完毕后,表 B 会包含表 A 中每个客户编号的副本,在 INSERT 期间不会发生错误。
ON EXCEPTION 语句的放置中的第二个示例使用 WITH RESUME 关键字表示在 ON EXCEPTION 语句中的语句块执行后,在 SELECT COUNT(*) FROM emp_list 语句继续执行,该语句是跟随在出现错误的行之后的行。对于此函数,其结果是即使出现错误也计数销售人员姓名。
在出现异常后继续执行
如果省略 WITH RESUME 关键字,则在出现异常后执行的下一个语句取决于 ON EXCEPTION 语句的位置,如以下应用场合所描述的:
- 如果 ON EXCEPTION 语句在具有 BEGIN 和 END 关键字的语句块中,则在对该 BEGIN ... END 块后的第一个语句(如果有)恢复执行,即,在 ON EXCEPTION 语句的作用域后恢复。
- 如果 ON EXCEPTION 语句在循环(FOR 、WHILE 、FOREACH)内部,则跳过循环的其余部分,并在循环的下一迭代时恢复执行。
- 如果没有语句块,而仅有 SPL 例程包含 ON EXCEPTION 语句,那么该例程执行不带参数的 RETURN 语句,返回成功状态而不返回值。
如果在语句块执行期间出现错误,要防止无限循环,则搜索另一个 ON EXCEPTION 语句以俘获错误不包括搜索当前的 ON EXCEPTION 语句。
相关语句
RAISE EXCEPTION
RAISE EXCEPTION
使用 RAISE EXCEPTION 语句模拟错误的生成。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
error_text | SPL 变量或表达式,包含错误文本 -746 的错误消息 | 必须是字符数据类型并且在语句块中有效 | 标识符; 表达式 |
ISAM_error | SPL 变量或代表 ISAM 错误号的表达式SPL。缺省值为 0。 | 必须返回 SMALLINT 范围中的值。可以在错误号前指定一个一元减号 | 表达式 |
SQL_error | SPL 变量或表达式,代表 SQL 错误号 | 同 ISAM_error | 表达式 |
用法
使用 RAISE EXCEPTION 语句模拟错误或通过定制消息生成错误。ON EXCEPTION 语句可捕捉生成的异常。
如果省略 ISAM_error,则当出现异常时,数据库服务器将 ISAM 错误代码设置为零(0)。如果希望指定 error_text ,但不指定 ISAM_error 值,则指定零(0)为 ISAM_error 的值。
RAISE EXCEPTION 语句可以产生系统生成的异常或用户生成的异常。例如,以下语句产生错误号 -208:
RAISE EXCEPTION -208, 0;
此处在 EXCEPTION 关键字后需要减号(- )用于错误 -208;大多数错误代码是负整数。
特殊的错误号 -746
特殊的错误号 -746 允许您生成定制的消息。例如,以下语句产生错误号 -746 并返回引号中的文本:
RAISE EXCEPTION -746, 0, 'You broke the rules';
在下列示例中,alpha 的负值产生异常 -746 并提供描述该问题的特定消息。代码中应包含 ON EXCEPTION 语句,它俘获异常 -746。
FOREACH SELECT c1 INTO alpha FROM sometable
IF alpha < 0 THEN
RAISE EXCEPTION -746, 0, 'a < 0 found' -- emergency exit
END IF
END FOREACH
当执行 SPL 例程并满足 IF 条件时,数据库服务器返回以下错误:
-746: a < 0 found.
有关作用域和异常兼容性的更多信息,请参阅 ON EXCEPTION。
相关语句
ON EXCEPTION
RETURN
使用 RETURN 语句来指定 SPL 函数将哪些值(如果有的话)返回给调用上下文。
语法
用法
在 GBase 8s 中,对于向后兼容性,您可以在 CREATE PROCEDURE 语句中使用 RETURN 语句创建 SPL 函数。然而,通过在 CREATE FUNCTION 语句中只使用 RETURN ,您可以保留使用 CREATE FUNCTION 来定义返回值的例程的约定,并为其它例程保留 CREATE PROCEDURE。
SPL 函数中的所有 RETURN 语句必须和定义该函数的 CREATE FUNCTION (或 CREATE PROCEDURE)语句的 RETURNING 子句相一致。表达式的任何 RETURN 列表在基数上必须与函数定义的 RETURNING 子句的数据类型的有序列表相匹配,并且在数据类型方面必须与其兼容。
或者,即使 RETURNING 子句列出了一个或多个数据类型,RETURN 语句也不能指定表达式。在这种情况下,没有指定表达式的 RETURN 语句等于将预期的 NULL 值的数目返回给调用上下文。只有将 SPL 函数声明为不返回任何值时,不带有任何表达式的 RETURN 语句才可以存在。否则,它返回 NULL 值。
以下 SPL 函数有两个有效的 RETURN 语句:
CREATE FUNCTION two_returns (stockno INT) RETURNING CHAR (15);
DEFINE des CHAR(15);
ON EXCEPTION (-272) -- if user does not have select privilege
RETURN; -- return no values.
END EXCEPTION;
SELECT DISTINCT descript INTO des FROM stock
WHERE stock_num = stockno;
RETURN des;
END FUNCTION;
在前面的示例中调用函数的程序应测试是否没有返回值并进行相应的操作。
WITH RESUME 关键字
如果使用 WITH RESUME 关键字,则在执行 RETURN 语句后,SPL 函数的下一个调用(对下一个 FETCH 或 FOREACH 语句)从跟随 RETURN 语句的语句开始。任何执行 RETURN WITH RESUME 语句的函数必须在 FOREACH 循环或 SELECT 的 FROM 子句中被调用。如果 SPL 例程执行 RETURN WITH RESUME 语句,则 GBase 8s ESQL/C 应用程序中的 FETCH 语句可以调用 SPL 例程。
以下示例显示另一个 UDR 可调用的游标函数。在 RETURN WITH RESUME 语句将每个值返回到调用的 UDR 或程序后,在下一次调用 series 时执行 series 的下一行。如果变量 backwards 等于零(0),则没有值返回给调用的 UDR 或程序,并停止 series 的执行:
CREATE PROCEDURE sensitive_update()
...
LET mailcall = 'mail headhoncho < alert';
-- code to execute if user tries to execute a specified
-- command, then sends email to system administrator
SYSTEM mailcall;
...
END PROCEDURE; -- sensitive_update
可以使用双竖线符号(||)将表达式与 SYSTEM 语句连接起来,如以下示例中所示:
CREATE PROCEDURE sensitive_update2()
DEFINE user1 char(15);
DEFINE user2 char(15);
LET user1 = 'joe';
LET user2 = 'mary';
...
-- code to execute if user tries to execute a specified
-- command, then sends email to system administrator
SYSTEM 'mail -s violation' || user1 || ' ' || user2|| '< violation_file';
...
END PROCEDURE; --sensitive_update2
从另一个数据库返回值
如果 SPL 函数使用 Return 子句从本地 GBase 8s 实例的另一个数据库返回值,则支持将以下数据类型作为返回的数据类型:
- 不透明的内置数据类型
- 大多数内置透明数据类型,在跨数据库事务中的数据类型 中列出
- 以上两行中引用的内置类型的 DISTINCT
- 此列表中任何 DISTINCT 数据类型的 DISTINCT
- 显式转换为此列表中某个内置数据类型的任何不透明用户定义类型(UDT)。
UDF 以及类型层次结构、转型、DISTINCT 类型和 UDT 的定义在每个参与数据库中必须完全相同。相同的数据类型限制适用于外部函数从本地 GBase 8s 实例的另一个数据库返回的值。有关跨同一数据库服务器的两个或多个数据库的分布式操作中支持的数据类型的详细信息,请参阅跨数据库事务中的数据类型。
UDR 从另一个数据库服务器的表中只能返回以下数据类型:
- 任何不透明的内置数据类型
- BOOLEAN
- LVARCHAR
- 不透明数据类型 DISTINCT
- BOOLEAN DISTINCT
- LVARCHAR DISTINCT
- 此列表中出现的任何 DISTINCT 类型的 DISTINCT
只有在 DISTINCT 类型显式转换为内置类型时,UDR 才能从其它 GBase 8s 实例的数据库返回这些 DISTINCT 类型。DISTINCT 数据类型的定义,它们的类型层次结构,以及它们的强制转型在参与分布式操作的数据库中必须完全相同。对于使用上一列表中的数据类型作为参数或返回数据类型的跨服务器 UDR 中的查询或其它 DML 操作,必须在每个参与数据库中定义 UDR ,参与的 GBase 8s 实例必须支持数据类型为跨服务器操作中的返回值。
有关 GBase 8s 可在分布式操作中访问的数据类型的其它信息,请参阅分布式查询中的数据类型。
外部函数和迭代器函数
在 SPL 程序中,如果外部函数不是迭代器函数,则可以使用 C 或 Java™ 语言外部函数作为 RETURN 语句的表达式。迭代器函数是返回一行或多行数据的外部函数(因而需要游标来执行)。
SPL 迭代函数必须包含 RETURN WITH RESUME 语句。有关在查询的 FROM 子句中通过虚拟表接口使用迭代器函数的信息,请参阅迭代器函数。
SYSTEM
使用 SYSTEM 语句从 SPL 例程中发出操作系统命令。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
expression | 对用户可执行操作系统命令求值 | 不能指定该命令在后台运行 | 操作系统从属 |
SPL_var | 包含命令的 SPL 变量 | 必须是字符数据类型 | 标识符; |
用法
如果指定的 expression 不是字符表达式,则它被转换为字符表达式并传递到操作系统以进行执行。
SYSTEM 指定的命令不能在后台运行。数据库服务器在继续 SPL 例程中的下一语句之前等待操作系统完成该命令的执行。SPL 例程不能使用任何从该命令返回的值。
如果操作系统命令失败(即,返回命令的非零状态),则出现异常。该异常包含返回的操作系统状态作为 ISAM 错误代码和相应的 SQL 错误代码。
回滚不终止系统调用,这样暂挂的事务可以无限等待调用以返回。有关在长事务回滚期间从死锁恢复的指示信息,请参阅 GBase 8s 管理员指南。
GBase 8s 的动态日志功能自动添加日志文件,直到长事务成功完成或回滚。
在包含 SYSTEM 语句的 DBA 和所有者特权的 SPL 例程中,该命令经执行例程的用户的许可方可运行。
在 UNIX 上执行 SYSTEM 语句
在 UNIX™ 平台的 SPL 过程中,评估为有效 UNIX 操作系统命令的规范必须紧跟在 SYSTEM 关键字之后。
这两个程序片段使用 SPL 的 SYSTEM 语句向系统管理员发送消息:
- 在第一个示例中,sensitive_update 例程定义名为 mailcall 的 SPL 变量存储指定 mail 实用程序名称,邮件的收件人的用户 ID 和邮件文本的字符串。
- 在第二个示例中,sensitive_update2 例程类似地使用 SYSTEM 语句调用 mail 实用程序。表达式通过连接三个带引号的字符串和 SPL 变量 user1 和 user2 来构造有效的命令行,以向系统管理员发送一个名为 violations_file 的文件。
使用 SYSTEM 语句发送邮件
在 SPL 例程的以下示例中的 SYSTEM 语句可使 UNIX 操作系统将邮件消息发送给系统管理员,他的用户 ID 是 headhoncho:
CREATE PROCEDURE sensitive_update()
...
LET mailcall = 'mail headhoncho < alert';
-- code to execute if user tries to execute a specified
-- command, then sends email to system administrator
SYSTEM mailcall;
...
END PROCEDURE; -- sensitive_update
可以使用双竖线符号(||)将表达式与 SYSTEM 语句连接起来,如以下示例中所示:
CREATE PROCEDURE sensitive_update2()
DEFINE user1 char(15);
DEFINE user2 char(15);
LET user1 = 'joe';
LET user2 = 'mary';
...
-- code to execute if user tries to execute a specified
-- command, then sends email to system administrator
SYSTEM 'mail -s violation' || user1 || ' ' || user2|| '< violation_file';
...
END PROCEDURE; --sensitive_update2
在以上两个示例中,空格分隔命令行的元素,因此 SYSTEM 关键字后面的表达式计算为符合操作系统 mail 实用程序的语法要求的字符串。
在 Windows 上执行 SYSTEM 语句
在 Windows™ 系统中,只有当正在执行 SPL 例程的当前用户已经用密码登录后,才能在 SPL 例程中执行任何 SYSTEM 语句。
数据库服务器必须拥有用户的密码和登录名以代表该用户的执行命令。
以下 SPL 例程示例中的第一个 SYSTEM 语句可使 Windows 将错误消息发送给临时文件并将消息放入按字母排序的系统日志中。第二个 SYSTEM 语句导致操作系统删除临时文件:
CREATE PROCEDURE test_proc()
...
SYSTEM 'type errormess101 > %tmp%tmpfile.txt |
sort >> %SystemRoot%systemlog.txt';
SYSTEM 'del %tmp%tmpfile.txt';
...
END PROCEDURE; --test_proc
在此示例中跟随 SYSTEM 语句的表达式包含由 Windows 定义的变量 %tmp% 和 %SystemRoot%。
在 SYSTEM 命令中设置环境变量
当执行 SYSTEM 指定的操作系统命令时,不存在任何用户应用程序设置的环境变量被传递到操作系统的保证。如果在 SYSTEM 命令中设置环境变量,则该设置仅在该 SYSTEM 命令期间有效。
要避免此潜在问题,建议使用以下方法确保用户应用程序要求的任何环境变量被转发到操作系统。
更改操作系统命令的环境设置:
- 创建将会设置希望环境的 shell 脚本(在 UNIX™ 系统上)或 batch 文件(在 Windows™ 平台上),然后执行操作系统命令。
- 使用 SYSTEM 命令执行 shell 脚本或 batch 文件。
此解决方案还有其它优势:如果您以后需要更改环境,可以修改 shell 脚本或 batch 文件,而不需重新编译 SPL 例程。
有关设置环境变量的操作系统命令的信息,请参阅 《GBase 8s SQL 指南:参考》。
TRACE
使用 TRACE 语句来控制调试输出的生成。
语法
用法及限制
TRACE 语句生成输出,该输出会发送给 SET DEBUG FILE TO 语句指定的文件。跟踪以下程序对象的当前值写入到调试文件:
- SPL 变量
- 例程参数
- 返回值
- SQL 错误代码
- ISAM 错误代码
每个执行的 TRACE 语句的输出以独立行显示。
如果在指定 DEBUG 文件以包含输出前使用 TRACE 语句,则会生成错误。
SPL 例程调用的任何例程继承跟踪状态。即,调用的例程(在相同的数据库服务器上)假设相同的跟踪状态(ON 、OFF 或 PROCEDURE)作为调用例程。调用的例程可设置其子句的跟踪状态,但不将该状态传递回调用的例程。
在远程数据库服务器上执行的例程不继承跟踪状态。
TRACE ON
如果指定关键字 ON,则跟踪所有语句。在使用变量(在表达式中或其它)的值之前打印它们。将跟踪设置为 ON 表示例程主体中的例程调用和语句都被跟踪。
TRACE OFF
如果您指定关键字 OFF,则所有的跟踪都被关闭。
TRACE PROCEDURE
如果指定关键字 PROCEDURE,则仅跟踪例程调用和返回值,而非例程主体。
TRACE 语句没有 ROUTINE 或 FUNCTION 关键字。因为即使想要跟踪的 SPL 例程是一个函数,也使用 TRACE PROCEDURE 关键字。
显示表达式
可以使用具有引号引起的字符串或表达式的 TRACE 语句显示输出文件中的值或注释。如果表达式不是字符表达式,则在写入输出文件之前计算表达式的值。
即使在例程中较早地使用了 TRACE OFF 语句,也可以使用具有表达式的 TRACE 语句。但必须首先使用 SET DEBUG 语句建立一个跟踪输出文件。
下一个示例在使用 TRACE OFF 语句之后使用具有表达式的 TRACE 语句。该示例使用了 UNIX™ 文件命名约定:
CREATE PROCEDURE tracing ()
DEFINE i INT;
BEGIN
ON EXCEPTION IN (1)
END EXCEPTION; -- do nothing
SET DEBUG FILE TO '/tmp/foo.trace';
TRACE OFF;
TRACE 'Forloop starts';
FOR i IN (1 TO 1000)
BEGIN
TRACE 'FOREACH starts';
FOREACH SELECT...INTO a FROM t
IF <some condition> THEN
RAISE EXCEPTION 1 -- emergency 退出
END IF
END FOREACH -- 返回值
END
END FOR
END;
END PROCEDURE
显示不同格式的 TRACE 的示例
以下示例显示了几种不同格式的 TRACE 语句。该示例使用了 Windows™ 文件命名约定:
CREATE PROCEDURE testproc()
DEFINE i INT;
SET DEBUG FILE TO 'C:\tmp\test.trace';
TRACE OFF;
TRACE 'Entering foo';
TRACE PROCEDURE;
LET i = test2();
TRACE ON;
LET i = i + 1;
TRACE OFF;
TRACE 'i+1 = ' || i+1;
TRACE 'Exiting testproc';
SET DEBUG FILE TO 'C:\tmp\test2.trace';
END PROCEDURE
查看跟踪输出
要查看跟踪输出,请使用文本编辑器或类似的实用程序显示或阅读文件内容。
WHILE
使用该建立具有变量结束条件的循环。
语法
元素 | 描述 | 限制 | 语法 |
---|---|---|---|
label | 此循环的循环标签的名称 | 在此 SPL 例程的标签中必须是唯一的 | 标识符 |
用法
在 statement block 第一次运行和每个后续迭代之前计算 condition 的值。只要 condition 保留为 true ,迭代继续。当 condition 的值不为 true 时,循环终止。
如果 condition 中的任何表达式求值为 NULL,则 condition 不为真,除非您正在用 IS NULL 运算符对 NULL 显式地进行测试。
如果 condition 中的表达式因为引用了未初始化的 SPL 变量而具有 UNKNOWN 关键字,则会导致错误。在这种情况下,循环终止,出现异常。
SPL 例程中的 WHILE 循环的示例
以下示例说明了在 SPL 例程中的 WHILE 循环的使用。在 SPL 例程中,simp_while(第一个 WHILE 循环)执行 DELETE 语句。第二个 WHILE 循环执行 INSERT 语句并增加 SPL 变量的值。
CREATE PROCEDURE simp_while()
DEFINE i INT;
WHILE EXISTS (SELECT fname FROM customer
WHERE customer_num > 400)
DELETE FROM customer WHERE id_2 = 2;
END WHILE;
LET i = 1;
WHILE i < 10
INSERT INTO tab_2 VALUES (i);
LET i = i + 1;
END WHILE;
END PROCEDURE;
Labeled WHILE 循环
要创建 Labeled WHILE 循环,您可以在初始的 WHILE 关键字之前声明循环标签,并在 END WHILE 关键字之后重复此标签,如以下两个 WHILE 循环的示例:
CREATE PROCEDURE ex_cont_ex()
DEFINE i,s,j, INT;
<<while_jlab>>
WHILE j < 20
IF j > 10 THEN
CONTINUE WHILE;
END IF
LET i,s = j,0;
<<while_slab>>
WHILE i > 0
LET i = i -1;
IF i = 5 THEN
EXIT while_jlab;
END IF
END WHILE while_slab
END WHILE while_jlab
END PROCEDURE;
此处的 EXIT while_jlab 语句与 EXIT 或 EXIT FOR 关键字具有相同的效果,终止外部 WHILE 循环和例程。在这个例子中,包括 EXIT while_jlab 语句的语句具有与 EXIT while_jlab WHEN i = 5 相同的效果。
您还可以标记以紧跟在初始 WHILE 关键字和条件之前的循环 <
您可以使用类似的语法创建一个未标记的循环,省略紧跟在 WHILE 条件规范之前的 <
相关语句
<< Label >> 语句 、CONTINUE 、EXIT 、LOOP
AUTONOMOUS_TRANSACTION
自治事务是由另一个事务(主事务)启动的独立事务,它在执行SQL操作并提交或回滚,而无需提交或回滚主事务。并且其还具有如下特点:
自治事务不与主事务共享事务资源(如锁)。
自治事务不依赖于主事务。例如,如果主事务回滚,则嵌套事务会回滚,但自治事务不会回滚。
自治事务提交的更改会立即对其他事务可见。
自治事务的异常部分属于外部事务,自治事务中引发的异常会导致事务级回滚,而非语句级回滚。
语法
用法及限制
-
ORACLE模式下运行。
-
声明部分将例程标记为自治事务。
-
应用场景包括:存储过程、函数、包、触发器。
-
不支持重复声明自治事务。
-
不支持在嵌套块内声明自治事务。
-
当进入自治例程的可执行部分时,主事务暂停。回退自治例程后,主事务将恢复。
-
COMMIT并结束活动的自治事务,但例程未执行结束。
-
保存点的作用域是在其中定义它的事务。事实上,主事务和自治事务可以使用相同的保存点名称。
-
自治例程依然属于当前会话,如时间格式、序列等以会话粒度使用的功能。自治例程与外部保持一致。
-
会话级隔离级别设置,自治事务设置后影响外部主事务事务属性,与其共享。
-
事务级隔离级别或属性设置,自治事务设置仅影响本事务,不影响外部主事务。亦或是自治例程内commit后由下一个语句开启的新事务,同样不受影响。
示例
create table employees(employee_id varchar(64),amount int,salary int);
insert into employees values(100,100,1000);
set environment sqlmode 'oracle';
创建plsql匿名块
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
emp_id NUMBER(6) := 100;
amount NUMBER(6,2) := 200;
BEGIN
UPDATE employees SET salary = salary - amount
WHERE employee_id = emp_id;
COMMIT;
END;
/
创建存储过程
CREATE OR REPLACE PROCEDURE lower_salary
(emp_id NUMBER, amount NUMBER)
AUTHID DEFINER AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE employees
SET salary = salary - amount
WHERE employee_id = emp_id;
COMMIT;
END lower_salary;
/
创建包
CREATE OR REPLACE PACKAGE emp_actions AUTHID DEFINER AS
FUNCTION raise_salary (emp_id NUMBER, sal_raise NUMBER)
RETURN NUMBER;
END emp_actions;
/
CREATE OR REPLACE PACKAGE BODY emp_actions AS
FUNCTION raise_salary (emp_id NUMBER, sal_raise NUMBER)
RETURN NUMBER IS
PRAGMA AUTONOMOUS_TRANSACTION;
new_sal NUMBER(8,2);
BEGIN
UPDATE employees SET salary =
salary + sal_raise WHERE employee_id = emp_id;
COMMIT;
SELECT salary INTO new_sal FROM employees
WHERE employee_id = emp_id;
RETURN new_sal;
END raise_salary;
END emp_actions;
/
创建触发器
CREATE TABLE log(
log_id NUMBER(6),up_date DATE,new_sal NUMBER(8,2),old_sal NUMBER(8,2));
CREATE TABLE emp(empno varchar(32),sal int);
CREATE OR REPLACE TRIGGER log_sal
BEFORE UPDATE OF sal ON emp FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log (
log_id,
up_date,
new_sal,
old_sal
)
VALUES (
:old.empno,
SYSDATE,
:new.sal,
:old.sal
);
--COMMIT;
END;
/