跳到主要内容

SQL 编程

前面的示例似乎将 SQL 作为一种交互的计算机语言;即,似乎您可以直接将 SELECT 语句输入数据库服务器内并查看回滚给您的数据行。

当然,情况并非如此。在您与数据库服务器之间存在许多软件层。在可显示数据之前,数据库服务器以必须格式化的二进制形式保留数据。它不会立即返回大量数据;当程序请求它时,它一次返回一行。

您可使用 DB-Access 通过交互的访问,通过以诸如 GBase 8s ESQL/C这样的 SQL API 编写的应用程序,或通过诸如 SPL 这样的应用程序语言,来访问您的数据库中的信息。

几乎所有程序都可包含 SQL 语句,执行它们,并从数据库服务器检索数据。本章节说明如何执行这些活动并指示您可如何编写执行它们的程序。

本章节介绍使用任何语言进行 SQL 编程的常见概念。在您可以特定的编程语言编写成功的程序之前,您必须先熟悉那种语言。然后,由于每种语言中处理的细节都不同,因此您必须熟悉特定于那种语言的 GBase 8s SQL API 的出版物。

程序中的 SQL

您可以任意几种语言编写程序,并将 SQL 语句混合在程序的其他语句之中,就如同它们是那种编程语言的一般语句似的。将这些 SQL 语句嵌入在程序中,且该程序包含嵌入式 SQL,其通常缩写为 ESQL。

SQL API 中的 SQL

ESQL 产品为 GBase 8s SQL API(应用程序编程接口)。 GBase 为 C 编程语言产生 SQL API。

下图展示 SQL API 产品如何工作。您编写您在其中将 SQL 语句处理作为可执行代码的源程序。嵌入式 SQL 预处理器处理您的源程序,它是一个定位嵌入式 SQL 语句并将它们转换为一系列过程调用和特殊的数据结构的程序。

图: 使用嵌入式 SQL 语句处理程序的概述

然后,转换了的源程序传递到编程语言编译器。在将它与静态的或动态的 SQL API 过程库相链接之后,编译器输出称为可执行的程序。当程序运行时,调用该 SQL API 库过程;它们与数据库服务器建立通信来执行 SQL 操作。

如果您将您的可执行程序链接到线程库包,则您可开发 GBase 8s ESQL/C 多线程应用程序。多线程应用程序可具有控制的许多线程。它将一个进程分割成多个执行线程,每一线程独立地运行。多线程的 GBase 8s ESQL/C 应用程序的主要优势在于,每一线程可同时具有与数据库服务器的许多活动的连接。而非线程的 GBase 8s ESQL/C 应用程序可创建与一个或多个数据库的许多连接,它一次仅可有一个连接是活动的。对于多线程的 GBase 8s ESQL/C 应用程序,每一线程可有一个活动的连接,且每个应用程序可有许多线程。

要获取关于多线程的应用程序的更多信息,请参阅《GBase 8s ESQL/C 程序员手册》。

应用程序语言中的 SQL

尽管 GBase 8s SQL API 产品允许您将 SQL 嵌入在主语言中,但某些语言包括 SQL 作为它们的语句集的固有部分。GBase 8s “存储过程语言”(SPL)使用 SQL 作为它的语句集的固有部分。您使用 SQL API 产品编写应用程序。您使用 SPL 编写例程,例程与数据库一起存储并从应用程序调用它。

静态的嵌入

您可通过静态的嵌入 或动态的嵌入将 SQL 语句引入程序。比较简单和常见的方式是通过静态的嵌入,其意味着编写 SQL 语句作为代码的一部分。该语句为静态的,是因为它们是源文本的固定部分。要获取关于静态的嵌入的更多信息,请参阅 检索单行 和 检索多行。

动态的语句

有些应用程序要求有动态地组合 SQL 语句的能力,以响应用户输入。例如,程序可能必须选择不同的列或将不同的标准应用到行,这依赖于用户想要什么。

使用动态的 SQL,程序在内存中将 SQL 语句组合成字符串,并将它传递给数据库服务器来执行。动态的语句不是代码的一部分;在执行期间在内存中构造它们。要了解更多信息,请参阅 动态 SQL。

程序变量和主变量

应用程序可在 SQL 语句内使用程序变量。在 SPL 中,当语法允许时,您可在 SQL 语句中放置程序变量。例如,DELETE 语句可在它的 WHERE 子句中使用程序变量。

下列代码示例展示 SPL 中的程序变量。

CREATE PROCEDURE delete_item (drop_number INT)

DELETE FROM items WHERE order_num = drop_number

在使用嵌入式 SQL 语句的应用程序中,SQL 语句可引用程序变量的内容。在嵌入式 SQL 语句中命名的程序变量称为主变量,因为在程序中将该 SQL 语句认作是客人。

下列示例展示 DELETE 语句,当将它嵌入在 GBase 8s ESQL/C 源程序中时,它可能出现:

EXEC SQL delete FROM items
WHERE order_num = :onum;

在此程序中,您看到常规的 DELETE 语句,如同 修改数据 描述的那样。当执行该 GBase 8s ESQL/C 程序时,删除 items 表的一行;还可删除多行。

该语句包含一个新的特性。它将 order_num 列与编写为 :onum 的一项相比较,这是主变量的名称。

SQL API 产品提供一种分隔主变量的名称的方式,当它们出现在 SQL 语句的上下文中时。在 GBase 8s ESQL/C 中,可使用美元符号($)或冒号(:)来引入主变量。冒号是符合 ANSI 的格式。示例语句请求数据库服务器删除其中的订单编号等于名为 :onum 的主变量的当前内容的那些行。在程序中声明了此数值变量并提前分配了值。

在 GBase 8s ESQL/C 中,可使用前导的美元符号($)或关键字 EXEC SQL 来引入 SQL 语句。

在前面的示例中说明的语法的差异很小; 重要的是,SQL API 和 SPL 语言使您执行下列任务:

  • 在源程序中嵌入 SQL 语句,就好像它们是主语言的可执行语句一样。
  • 以使用文字值的方式,在 SQL 表达式中使用程序变量。

如果您有编程经验,则您可立即看到这些可能性。在该示例中,将要删除的订单号码传到变量 onum 中。那个值来自程序可使用的任何源。可从文件读取它,程序可提示用户输入它,或可从数据库读取它。DELETE 语句本身可为子例程的一部分(在此情况下,onum 可为该子例程的参数);可一次或反复地调用该子例程。

总之,当您在程序中嵌入 SQL 语句时,您可对它们应用主语言的所有功能。您可将 SQL 语句隐藏在许多接口之下,且可以多种方式优化 SQL 函数。

调用数据库服务器

执行 SQL 语句本质上是作为子例程调用数据库服务器。必须将信息从程序传到数据库服务器,且必须将信息从数据库服务器返回到程序。

此通讯的部分是通过主变量完成的。您可将在 SQL 语句中命名的主变量视作对数据库服务器调用的过程的参数。在前面的示例中,主变量作为 WHERE 子句的参数。主变量收到数据库服务器返回的数据,如同 检索多行 描述的那样。

SQL 通信区域

数据库服务器始终在一个称为“SQL 通信区域”(SQLCA)的数据结构中返回结果代码,以及关于操作结果的其他可能信息。如果数据库服务器在用户定义的例程中执行 SQL 语句,则调用应用程序的 SQLCA 包含在该例程中 SQL 语句触发的值。

在从 表 1 至 表 1 中罗列 SQLCA 的主体字段。在编程语言之中,您用来描述诸如 SQLCA 这样的数据结构的语法,以及您用来应用其中字段的语法是不同的。要了解详细信息,请参阅您的 SQL API 出版物。

特别地,您通过其命名 SQLERRD 和 SQLWARN 数组的一个元素的下标是不同的。在 GBase 8s ESQL/C 中,数组元素从零开始编号,但在其他语言中,从一开始。在本讨论中,以诸如 third 这样的特定词命名字段,且您必须将这些词翻译成您的编程语言的语法。

您还可使用 GET DIAGNOSTICS 语句的 SQLSTATE 变量来检测、处理和诊断错误。请参阅 SQLSTATE 值。

SQLCODE 字段

SQLCODE 字段是数据库服务器的主要返回代码。在每个 SQL 语句之后,将 SQLCODE 设置为如下表所示的一个整数值。当那个值为零时,无误地执行该语句。特别地,当假定一个语句将数据返回到主变量内时,代码零意味着已返回了该数据且可使用它。任何非零代码都意味着相反的意思。未将有用的数据返回到了主变量。

表 1. SQLCODE 的值

返回值解释
值 < 0指定一个错误代码。
值 = 0指示成功。
0 < 值 < 100在 DESCRIBE 语句之后,表示描述 SQL 语句的类型的一个整数值。
100在未返回行的成功查询之后,指示 NOT FOUND 条件。在 INSERT INTO/SELECT、UPDATE、DELETE 或 SELECT... INTO TEMP 语句未能访问任何行之后,NOT FOUND 还可发生在符合 ANSI 的数据库中。

数据的结束

当正确地执行语句,但未找到行时,数据库服务器将 SQLCODE 设置为 100。在两种情况下可发生此情况。

第一种情况涉及使用游标的查询。(检索多行 描述使用游标的查询。)在这些查询中,FETCH 语句将来自活动集的每一值检索到内存内。检索最后一行之后,后续的 FETCH 语句不可返回任何数据。当发生此情况时,数据库服务器将 SQLCODE 设置为 100,指示数据的结束,找不到行。

第二种情况涉及不使用游标的查询。在此情况下,当没有行满足查询条件时,数据库服务器将 SQLCODE 设置为 100。在不符合 ANSI 的数据库中,仅不返回行的 SELECT 语句会导致将 SQLCODE 设置为 100。

在符合 ANSI 的数据库中,如果未返回行,则 SELECT、DELETE、UPDATE 和 INSERT 语句都将 SQLCODE 设置为 100。

负代码

在语句期间,当发生意外错误时,数据库服务器在 SQLCODE 中返回一个负数值来说明该问题。在联机错误消息文件中记录这些代码的含义。

SQLERRD 数组

在 SQLCODE 中可报告的某些错误代码反映一般的问题。数据库服务器可在 SQLERRD 的第二个字段中设置更详细的代码,显示数据库服务器 I/O 例程或操作系统遇到的错误。

将 SQLERRD 数组中的整数设置为跟在不同语句之后的不同值。在 GBase 8s ESQL/C 中,仅使用数组的第一个和第四个元素。下表展示如何使用这些字段。

表 1. SQLERRD 的字段

字段解释
第一个对于 SELECT、UPDATE、INSERT 或 DELETE 语句,在成功的 PREPARE 语句之后,或在打开 Select 游标之后,此字段包含估计的受影响的行数。
第二个当 SQLCODE 包含一个错误代码时,此字段包含零或附加的错误代码,称为 ISAM 错误代码,说明主要错误的原因。在对单个行的成功的插入操作之后,此字段包含任何 SERIAL、BIGSERIAL 或 SERIAL8 值为那行生成的值。(然而,当通过表上的触发器,或通过视图上的 INSTEAD OF 触发器,将一序列列作为触发器的活动直接插入时,不更新此字段。)
第三个在成功的多行插入、更新或删除操作之后,此字段包含处理了的行数。在以错误结束的多行插入、更新或删除操作之后,此字段包含在检测到该错误之前成功地处理了的行数。
第四个在对于 SELECT、UPDATE、INSERT 或 DELETE 语句的成功的 PREPARE 语句之后,或在已打开了选择游标之后,此字段包含磁盘访问的与处理的全部行的估计加权总和。
第五个在 PREPARE、EXECUTE IMMEDIATE、DECLARE 或静态的 SQL 语句中的语法错误之后,此字段包含检测到该错误的位置的语句文本的偏移量。
第六个在对选择了的行的成功的访存之后,或成功的插入、更新或删除操作之后,此字段包含处理了的最后一行的 rowid(物理地址)。此 rowid 值是否对应于数据库服务器返回给用户的行,依赖于数据库服务器处理查询的方式,特别是对于 SELECT 语句。
第七个保留。

这些附加的详细信息可是有用的。例如,您可使用第三个字段中的值来报告删除了或更新了多少行。当您的程序准备一个用户输入的 SQL 语句并发现错误时,第五个字段中的值使得您能够向用户显示错误的精确点。(当您在错误之后请求修改语句时,DB-Access 使用此特性来定位游标。)

SQLWARN 数组

将 SQLWARN 数组中的八个字符字段设置为空,或设置为 W 来指示各种特殊的情况。它们的含义依赖于刚刚执行的语句。

当数据库打开时,即,跟在 CONNECT、DATABASE 或 CREATE DATABASE 语句之后,出现一组警告标志。这些标志告诉您数据库的一些整体特征。

第二组标志跟在任何其他语句之后出现。这些标志反映在该语句期间发生的不寻常事件,这些事件通常没有严重到通过 SQLCODE 来反映的程度。

在下表中总结这两组 SQLWARN 值。

表 1. SQLWARN 的字段

字段当打开或连接到数据库时所有其他 SQL 操作
第一个当将任何其他警告字段设置为 W 时,设置为 W。如果为空,则不需要检查其他的。当将任何其他警告字段设置为 W 时,设置为 W。
第二个当现在打开的数据库使用事务日志时,设置为 W。如果截断列值,当使用 FETCH 或 SELECT...INTO 语句将它访存到主变量内时,设置为 W 。在 REVOKE ALL 语句上,当未取消全部七个表级别权限时,设置为 W。
第三个当现在打开的数据库符合 ANSI 时,设置为 W。当 FETCH 或 SELECT 语句返回为 NULL 的聚集函数(SUM、AVG、MIN、MAX)值时,设置为 W。
第四个当数据库服务器为 GBase 8s 时,设置为 W。在 SELECT ... INTO、FETCH ... INTO 或 EXECUTE ... INTO 语句上,当 projection 列表项数不同于在 INTO 子句中检索它们的主变量的数目时,设置为 W。在 GRANT ALL 语句上,当未授予全部七个表级别访问权限时,设置为 W。
第五个当数据库服务器以 DECIMAL 形式存储 FLOAT 数据类型时,设置为 W。当主机系统缺乏对 FLOAT 类型的支持时,这样做。如果准备好的对象包含不带有 WHERE 子句的 DELETE 语句或 UPDATE 语句时,设置为 W。
第六个保留。跟在不使用 ANSI 标准 SQL 语法的语句执行之后(假设设置了DBANSIWARN 环境变量),设置为 W。
第七个当将应用程序连接到在数据复制对中为辅助服务器的数据库服务器上时,设置为 W。即,该服务器仅对读取操作可用。在查询处理期间(当 DATASKIP 特性为 on 时),当已跳过了数据分片(dbspace)时,设置为 W。
第八个当客户机 DB_LOCALE 与数据库语言环境不相匹配时,设置为W。要获取更多信息,请参阅《GBase 8s GLS 用户指南》。当 SET EXPLAIN ON AVOID_EXECUTE 语句阻止查询执行时,设置为W。

SQLERRM 字符串

SQLERRM 可存储最多 72 字节的字符串。SQLERRM 字符串包含放置在错误消息里的标识符,诸如表名称。对于某些网络化的应用程序,它包含网络软件生成的错误消息。

如果由于违反约束导致 INSERT 操作失败,则将失败了的约束名称写到 SQLERRM。

提示

如果错误字符串长于 72 字节,则静默地废弃溢出的部分。在某些上下文中,这可导致关于运行时错误的信息丢失。

SQLSTATE 值

某些 GBase 8s 产品,诸如 GBase 8s ESQL/C,支持符合 X/Open 和 ANSI SQL 标准的 SQLSTATE 值。在您运行 SQL 语句之后,GET DIAGNOSTICS 语句读取 SQLSTATE 值来诊断错误。数据库服务器以称为 SQLSTATE 的变量中存储的五个字符的字符串来返回结果代码。SQLSTATE 错误代码,或值,告诉您关于最近执行的 SQL 语句的下列信息:

  • 该语句是否成功
  • 该语句是否成功但生成了警告
  • 该语句是否成功但未生成数据
  • 该语句是否失败了

要获取关于 GET DIAGNOSTICS 语句、SQLSTATE 变量以及 SQLSTATE 返回代码的含义的更多信息,请参阅《GBase 8s SQL 指南:语法》中的 GET DIAGNOSTICS 语句。

提示

如果您的 GBase 8s 产品支持 GET DIAGNOSTICS 和 SQLSTATE,则推荐您使用它们作为检测、处理和诊断错误的主要结构。使用 SQLSTATE 允许您检测多个错误,且它符合 ANSI。

检索单行

SELECT 语句返回的行集是它的活动集。单个 SELECT 语句返回单个行。您可使用嵌入式 SELECT 语句来从数据库将单个行检索到主变量内。然而,当 SELECT 语句返回多行数据时,程序必须使用游标来一次检索一行。在 检索多行 中讨论“多行”选择操作。

要检索单行数据,只要在您的程序中嵌入 SELECT 语句。下列示例展示您可如何使用 GBase 8s ESQL/C 来编写嵌入式 SELECT 语句:

EXEC SQL SELECT avg (total_price)
INTO :avg_price
FROM items
WHERE order_num in
(SELECT order_num from orders
WHERE order_date < date('6/1/98') );

INTO 子句是将此语句与 编写 SELECT 语句 或 编写高级 SELECT 语句 中的任何示例区分开来的唯一细节。此子句指定要检索产生的数据的主变量。

当程序执行嵌入式 SELECT 语句时,数据库服务器执行该查询。示例语句选择聚集值,以便于它恰好产生一行数据。该行仅有单个列,且它的值存储在名为 avg_price 的主变量中。程序的后续行可使用那个变量。

您可使用此类语句来将单行数据检索到主变量内。单个行可有所期望的许多列。如果查询产生多行数据,则数据库服务器不可返回任何数据,而是返回一个错误代码。

您在 INTO 子句中罗列的主变量应与选择列表中的项一样多。如果这些列表的长度碰巧不一样,则数据库服务器返回尽可能多的值并在 SQLWARN 的第四个字段中设置警告标志。

数据类型转换

下列 GBase 8s ESQL/C 示例检索 DECIMAL 列的平均值,其自身是 DECIMAL 值。然而,将 DECIMAL 列的平均值放置其内的主变量不要求具有那种数据类型。

EXEC SQL SELECT avg (total_price) into :avg_price
FROM items;

不展示在前面的 GBase 8s ESQL/C 代码示例中接收的变量 avg_price 的声明。该声明可为任一下列定义:

int avg_price;
double avg_price;
char avg_price[16];
dec_t avg_price; /* typedef of decimal number structure */

注释语句中使用的每一主变量的数据类型,并使用该语句传到数据库服务器。数据库服务器尽量将列数据转换为接收的变量使用的形式。允许几乎任何转换,尽管某些转换会导致精度损失。依赖于接收的主变量的数据类型,前面的示例的结果会不同,如下表所示。

数据类型结果
FLOAT数据库服务器将十进制结果转换为 FLOAT,可能截断某些小数位。如果十进制的数量超过 FLOAT 格式的最大数量,则返回一个错误。
INTEGER数据库服务器将结果转换为 INTEGER,如有必要会截断小数位。如果被转换的数值的整数部分与接收的变量不适合,则发生错误。
CHARACTER数据库服务器将十进制值转换为 CHARACTER 字符串。如果对于接收的变量来说该字符串太长,则截断它。将 SQLWARN 的第二个字段设置为 W,且 SQLSTATE 变量中的值为 01004。

如果程序检索到 NULL 值,该怎么办?

可在数据库中存储 NULL 值,但编程语言支持的数据类型不识别 NULL 状态。程序必须采用某种方式来识别 NULL 项,以免将它作为数据来处理。

在 SQL API 中,指示符变量满足此需要。指示符变量是与可能收到 NULL 项的主变量相关联的一个附加的变量。当数据库服务器将数据放在主变量中时,它还在指示符变量中放置一个特殊的值来展示该数据是否为 NULL。在下列 GBase 8s ESQL/C 示例中,选择单个行,并将单个值检索到主变量 op_date 内:

EXEC SQL SELECT paid_date
INTO :op_date:op_d_ind
FROM orders
WHERE order_num = $the_order;
if (op_d_ind < 0) /* data was null */
rstrdate ('01/01/1900', :op_date);

语句该值可能为 NULL,名为 op_d_ind 的指示符变量与该主变量相关联。(必须在程序中的其他地方将它声明为以短整数。)

跟在 SELECT 语句的执行之后,程序测试该指示符变量为负值。负值(通常为 -1)意味着检索到主变量内的值为 NULL。如果该变量为 NULL,则此程序使用 GBase 8s ESQL/C 库函数来将缺省的值指定给主变量。(函数 rstrdate 是 GBase 8s ESQL/C 产品的一部分。)

您用来将指示符变量与主变量相关联的语法,不同于您正在使用的语言,但在所有语言中,该原则是相同的。

处理错误

虽然数据库服务器自动地处理数据类型之间的转换,但使用 SELECT 语句仍可发生错误。在 SQL 编程中,如同在任何种类的编程中一样,您必须预见错误并随时为应对其做好准备。

数据的结束

通常会发生没有行满足查询的情况。在 SELECT 语句之后,通过 SQLSTATE 代码 02000 和 SQLCODE 中的代码 100 标志此事件。此代码指示一个错误或一般事件,这完全依赖于您的应用程序。如果您确信应有一行或多行满足查询(例如,如果您使用您刚从另一表的行读取的键值读取一行的话),则“数据的结束”代码表示在该程序的逻辑中的严重错误。在另一方面,如果您基于用户提供的键,或其他来源提供的不如程序可靠的键,则数据的缺乏可能是正常事件。

不符合 ANSI 的数据库的数据的结束

如果您的数据库不符合 ANSI,则跟在 SELECT 语句之后,仅在 SQLCODE 中设置“数据的结束”返回代码 100。此外,将 SQLSTATE 值设置为 02000。(诸如 INSERT、 UPDATE 和 DELETE 这样的其他语句设置 SQLERRD 的第三个元素,来展示他们影响了多少行;通过 SQL 程序修改数据 讨论此主题。)

严重的错误

将 SQLCODE 设置为负值的错误,或将 SQLSTATE 设置为任何不以 00、01 或 02 开头的值的错误通常都很严重。您已开发好的程序或正在生产的程序几乎不应报告这些错误。然而,很难预料每一种有问题的情况,因此您的程序必须能够处理这些错误。

例如,查询可返回错误 -206,这意味着在该查询中指定的表不在数据库中。如果有人在编写了程序之后删除了该表,或如果该程序通过某种逻辑错误或输入错误打开了错误数据库,则会发生此情况。

使用聚集函数解释数据的结束

使用诸如 SUM、MIN 或 AVG 这样的聚集函数的 SELECT 语句总会成功地返回至少一行数据,即使当没有行满足 WHERE 子句时。基于行的空集的聚集值为空,但它仍然存在。

然而,如果聚集值是基于都包含空值的一行或多行,则聚集值也为空。如果您必须能够检测不基于任何行的聚集值与基于某些都是空的行的聚集值之间的差异,则您必须在该语句中包括 COUNT 函数和聚集值上的指示符变量。然后,您可得出下列情况。

计数值指示符具体情况
0-1选择了零行
>0-1选择了某些行;全部为空
>00选择了一些非空行

缺省值

您可以许多方式处理这些不可避免的错误。在某些应用程序中,使用比执行功能更多的行代码来处理错误。然而,在本部分的示例中,最简单的解决方案之一,缺省值,应奏效,如下例所示:

avg_price = 0; /* 设置错误的缺省值 */
EXEC SQL SELECT avg (total_price)
INTO :avg_price:null_flag
FROM items;
if (null_flag < 0) /* 可能没有行 */
avg_price = 0; /* 设置 0 行的缺省值 */

前面的示例处理下列事项:

  • 如果查询选择一些非空的行,则返回并使用正确的值。这是期望的并且最常用的结果。
  • 如果查询没有选择行,或在发生的可能性很小的情况下,仅选择在 total_price 列(从不应为空的列)中有空值的那些行,则设置该指示符变量,并指定缺省值。
  • 如果发生任何严重的错误,则保持主变量不变;它包含最初设置的缺省值。在程序中的此点,程序员无需捕获这类错误并报告它们。

检索多行

当存在查询可返回多行的任何可能时,程序必须以不同的方式执行该查询。分两个阶段处理多行查询。首先,程序启动查询。(不立即返回数据。)然后,程序一次请求一行数据。

使用称为游标的特殊数据对象来执行这些操作。游标是表示查询的当前状态的数据结构。下表展示程序操作的一般的顺序:

  1. 程序声明游标及其相关联的 SELECT 语句,其只是分配持有该游标的存储。
  2. 程序打开游标,其启动相关联的 SELECT 语句的执行,并检测其中的任何错误。
  3. 程序将一行数据访存到主变量内,并处理它。
  4. 在访存最后一行之后,程序关闭游标。
  5. 当不再需要该游标时,程序释放游标来释放它使用的资源。

使用名为 DECLARE、OPEN、FETCH、CLOSE 和 FREE 的 SQL 语句执行这些操作。

声明游标

您使用 DECLARE 语句来声明游标。此语句给游标一个名称,指定它的使用,并将它与语句相关联。下列示例是用 GBase 8s ESQL/C 编写的:

EXEC SQL DECLARE the_item CURSOR FOR
SELECT order_num, item_num, stock_num
INTO :o_num, :i_num, :s_num
FROM items
FOR READ ONLY;

声明给游标一个名称(在此示例中为 the_item)并将它与 SELECT 语句相关联。(通过 SQL 程序修改数据 讨论还可如何与 INSERT 语句相关联。)

此示例中的 SELECT 语句包含 INTO 子句。INTO 子句指定哪个变量接收数据。您还可使用 FETCH 语句来指定哪个变量接收数据,如 定位 INTO 子句讨论的那样。

DECLARE 语句不是活动的语句;它仅仅创建游标的特性并为它分配存储。您可使用在前面示例中声明的游标来通读 items 表一次。可声明向后读和向前读游标(请参阅 游标输入模式)。由于此游标缺少 FOR UPDATE 子句,且由于指定 FOR READ ONLY,因此,它仅用于读取数据,不修改它。通过 SQL 程序修改数据 说明如何使用游标来修改数据。

打开游标

当程序准备使用游标时,它打开它。OPEN 语句激活游标。它将相关联的 SELECT 语句传给数据库服务器,其开始搜索相匹配的行。数据库服务器处理该查询至定位到或构造输出的第一行的位置。它并不真正地返回那行数据,但它确实在 SQLSTATE 中和在 SQLCODE 中为 SQL API 设置返回代码。下列示例展示 GBase 8s ESQL/C 中的 OPEN 语句:

EXEC SQL OPEN the_item;

由于数据库服务器正在第一次查看查询,因此,它可能检测到一些错误。在程序打开游标之后,它应测试 SQLSTATE 或 SQLCODE。如果 SQLSTATE 值大于 02000 或 SQLCODE 包含负值,则该游标不可用。在 SELECT 语句中可能出现错误,或某些其他问题可能阻止数据库服务器执行该语句。

如果 SQLSTATE 等于 00000,或 SQLCODE 包含零,则 SELECT 语句在语法上是有效的,且准备使用该游标。然而,此时,该程序不知道游标能否产生任何行。

访存行

程序使用 FETCH 语句来检索输出的每一行。此语句命名游标,且还可命名接收该数据的主变量。下列示例展示完整的 GBase 8s ESQL/C 代码:

EXEC SQL DECLARE the_item CURSOR FOR
SELECT order_num, item_num, stock_num
INTO :o_num, :i_num, :s_num
FROM items;
EXEC SQL OPEN the_item;
while(SQLCODE == 0)
{
EXEC SQL FETCH the_item;
if(SQLCODE == 0)
printf("%d, %d, %d", o_num, i_num, s_num);
}

检测数据的结束

在前面的示例中,WHILE 条件在 OPEN 语句返回错误时阻止执行循环。当将 SQLCODE 设置为 100 来标志数据的结束时,相同的条件会终止该循环。然而,该循环包含 SQLCODE 的测试。此测试是必需的,因为如果 SELECT 语句是有效的但找不到相匹配的行,则 OPEN 语句返回零,但第一次访存返回 100(数据的结束)并不返回任何数据。下列示例展示编写同一循环的另一种方式:

EXEC SQL DECLARE the_item CURSOR FOR
SELECT order_num, item_num, stock_num
INTO :o_num, :i_num, :s_num
FROM items;
EXEC SQL OPEN the_item;
if(SQLCODE == 0)
EXEC SQL FETCH the_item; /* fetch 1st row*/
while(SQLCODE == 0)
{
printf("%d, %d, %d", o_num, i_num, s_num);
EXEC SQL FETCH the_item;
}

在此版本中,早已处理了无返回行的情况,因此,在循环中不存在第二次 SQLCODE 测试。由于 SQLCODE 测试的时间成本是访存成本的很小一部分,因此这些版本在性能上没有多大差异。

定位 INTO 子句

INTO 子句命名要接收数据库服务器返回的数据的主变量。INTO 必须出现在 SELECT 或 FETCH 语句中。然而,它不可同时出现在两个语句中。下列示例指定 FETCH 语句中的主变量:

EXEC SQL DECLARE the_item CURSOR FOR
SELECT order_num, item_num, stock_num
FROM items;
EXEC SQL OPEN the_item;
while(SQLCODE == 0)
{
EXEC SQL FETCH the_item INTO :o_num, :i_num, :s_num;
if(SQLCODE == 0)
printf("%d, %d, %d", o_num, i_num, s_num);
}

此形式允许您将不同的行访存到不同的位置内。例如,您可以使用此形式来将连续的行访存到数组的连续元素内。

游标输入模式

为了输入,游标以顺序的或滚动的两种模式中的一种运行。顺序的游标仅可访存序列中的下一行,因此,每一次打开游标,顺序的游标仅可通读表一次。滚动游标可访存下一行或任何输出行,因此,滚动游标可多次读取相同的行。下列示例展示在 GBase 8s ESQL/C 中声明的顺序的游标。

EXEC SQL DECLARE pcurs cursor for
SELECT customer_num, lname, city
FROM customer;

在打开游标之后,仅可使用检索下一行数据的顺序的访存来使用它,如下例所示:

EXEC SQL FETCH p_curs into:cnum, :clname, :ccity;

每一顺序的访存返回一个新行。

使用关键字 SCROLL CURSOR 声明滚动游标,如来自 GBase 8s ESQL/C 的下列示例所示的那样:

EXEC SQL DECLARE s_curs SCROLL CURSOR FOR
SELECT order_num, order_date FROM orders
WHERE customer_num > 104

使用不同的访存选项来使用滚动游标。例如,ABSOLUTE 选项指定要访存的行的绝对行位置。

EXEC SQL FETCH ABSOLUTE :numrow s_curs
INTO :nordr, :nodat

此语句访存在主变量 numrow 中给出其位置的行。您还可在此访存当前的行,或您可访存第一行然后再次扫描所有行。然而,这些特性可能导致应用程序运行得更慢,如下一部分描述的那样。要了解适用于滚动游标的附加的选项,请参阅《GBase 8s SQL 指南:语法》 中的 FETCH 语句。

游标的活动集

一旦打开游标,就意味着选择一些行。查询产生的所有行的集合称为该游标的活动集。可以简单地将活动集视为定义良好的行的集合,且将游标视为指向该集合的一行。只要没有其他程序正在并发地修改同一数据,就会发生此情况。

创建活动集

当打开游标时,数据库服务器进行有必要的任何操作来定位选择了的数据的第一行。依赖于该查询的叙述方式,此活动可很容易,或者它可需要大量的工作和时间。请考虑下列游标的声明:

EXEC SQL DECLARE easy CURSOR FOR
SELECT fname, lname FROM customer
WHERE state = 'NJ'

由于在简单的方式中此游标仅需要单个表,因此,数据库服务器快速地确定是否有任何行满足该查询,并标识第一行。第一行是该游标此次找到的唯一一行。该活动集中余下的行仍然未知。作为对比,请考虑下列游标的声明:

EXEC SQL DECLARE hard SCROLL CURSOR FOR
SELECT C.customer_num, O.order_num, sum (items.total_price)
FROM customer C, orders O, items I
WHERE C.customer_num = O.customer_num
AND O.order_num = I.order_num
AND O.paid_date is null
GROUP BY C.customer_num, O.order_num

通过连接三个表并将输出行分组,生成此游标的活动集。优化器可能能够使用索引来以正确的顺序产生这些行,但通常情况下,ORDER BY 或 GROUP BY 子句的使用要求在可确定哪一行标识第一行之前,数据库服务器生成所有行,将它们复制到临时表并对该表排序。

在活动集全部生成并保存在临时表中的情况下,数据库服务器可花费相当多的时间来打开游标。接着,数据库服务器可以确切地告诉程序活动集包含多少行。然而,此信息不可用。一个原因是您永远不可确定优化器会使用哪种模式。如果优化器可避免排序和临时表,则它会这样做;但在查询中、在表的大小方面或在可用的索引方面的小更改都可更改优化器的方式。

顺序游标的活动集

数据库服务器尝试使用尽可能少的资源来维护游标的活动集。如果它可这么做,则数据库服务器从不保留下次访存的单个行之外的行。它可为大部分顺序的游标这么做。对于每一访存,它都返回当前行的内容并定位下一行。

SCROLL 游标的活动集

必须保留 SCROLL 游标的活动集中的所有行,直到游标关闭为止,因为数据库服务器不可确定程序下一次会请求哪一行。

更常见的是,数据库服务器将滚动游标的活动集作为临时表来实现。然而,数据库服务器可能不立即填充此表(除非它创建了临时表来处理该查询)。通常当打开游标时,它创建临时表。然后,第一次访存行时,数据库服务器将它复制到临时表内并将它返回到程序。当第二次访存行时,可从临时表取得它。如果在程序访存所有行之前,它放弃该查询,则此方案使用最少的资源。不创建或保存从不访存的行。

活动集和并发

当仅一个程序正在使用数据库时,活动集的成员不可更改。大多数个人计算机都是这种情况,且是要考虑的最简单情况。但必须为了在多编程系统中使用来设计一些程序,在此,两个、三个或几十个不同的程序可同时在相同的表上工作。

在您的游标是打开的时,当其他程序可更新表时,活动集的思路用处不大了。您的程序在某一时刻仅看到一行数据,但表中的所有其他行可能正在更改。

在简单查询的情况下,当数据库服务器仅持有活动集的一行时,任何其他行都可更改。在您的程序访存行之后的那一刻,另一程序可删除同一行或更新它,于是,如果在此检查它,它不再是活动集的一部分。

当在临时表中保存活动集或它的一部分时,旧数据可出现问题。即,从其派生活动集行的实际的表中的行可更改。如果真是这样,某些活动集行不再反映当前的表内容。

最初这些想法令人不安,但只要您的程序仅读取数据,就不存在旧数据,更确切地说,所有数据都同样陈旧。活动集是数据在某一时刻的快照。第二天行就不一样了;如果它在下一毫秒也不一样,倒无所谓。换言之,在程序正在运行时发生的更改,与该程序终止的那一刻保存和应用的更改之间,没有实际的差异。

旧数据可导致问题的唯一时刻,是当程序打算使用输入的数据来修改同一数据库时;例如,当银行业应用程序必须读取账户余额、更改它并将它写回时。通过 SQL 程序修改数据 讨论修改数据的程序。

部件爆炸问题

当您使用由程序逻辑补充的游标时,您可解决普通的 SQL 不可解决的问题。这些问题之一就是部件爆炸问题,有时称为材料单处理。此问题的核心是对象之间的递归关系;一个对象包含其他对象,其又包含其他对象。

通常以制造库存为例来说明该问题。例如,公司制造各种部件。有些部件是分立的,但有些是其他部件的组合。

在可能称为 contains 的单个表中说明这些关系。列 contains.parent 持有系组合的部件的部件编号。列 contains.child 具有为父部件的组件的部件的部件编号。如果部件编号 123400 是九个部件的组合,则存在九行,123400 在第一列中,其他部件编号在第二列中。下图展示描述部件编号 123400 的多行中的一行。

图: 部件爆炸问题

部件爆炸问题在于:给定一个部件编号,产生为那个部件的组件的所有部件的列表。下列示例是一种解决方案的概要,如以 GBase 8s ESQL/C 实现的那样:

int part_list[200];

boom(top_part)
int top_part;
{
long this_part, child_part;
int next_to_do = 0, next_free = 1;
part_list[next_to_do] = top_part;

EXEC SQL DECLARE part_scan CURSOR FOR
SELECT child INTO child_part FROM contains
WHERE parent = this_part;
while(next_to_do < next_free)
{
this_part = part_list[next_to_do];
EXEC SQL OPEN part_scan;
while(SQLCODE == 0)
{
EXEC SQL FETCH part_scan;
if(SQLCODE == 0)
{
part_list[next_free] = child_part;
next_free += 1;
}
}
EXEC SQL CLOSE part_scan;
next_to_do += 1;
}
return (next_free - 1);
}

从技术上讲,contains 表的每一行都是有向无环图,或树,的头结点。该函数执行对该树的宽度优先搜索,树根是作为它的参数传递的部件编号。该函数使用名为 part_scan 的游标返回在 parent 列中带有特定的值的所有行。最内层的 while 循环打开 part_scan 游标,在选择集中访存每一行,并当已检索了每一组件的部件编号时,关闭该游标。

此函数解决部件爆炸问题的核心,但该函数不是完整的解决方案。例如,它不允许组件在树中出现多个级别。此外,实际的 contains 表还会有列count,给出在每一 parent 中使用的 child 部件的计数。返回每一组件部件的总计数的程序要复杂得多。

之前描述的迭代方法不是解决部件爆炸问题的唯一方法。如果代的数目有固定的限制,则您可使用嵌套的外部自连接,以单个 SELECT 语句解决该问题。

如果在一个最高级别部件内,可包含最多四代部件,则下列 SELECT 语句返回所有部件:

SELECT a.parent, a.child, b.child, c.child, d.child
FROM contains a
OUTER (contains b,
OUTER (contains c, outer contains d) )
WHERE a.parent = top_part_number
AND a.child = b.parent
AND b.child = c.parent
AND c.child = d.parent

此 SELECT 语句为来源于指定为 top_part_number 的祖先的每一行返回一行。对于不存在的级别,返回 Null 值。(请使用指示符变量来检测它们。)要将此解决方案扩展到更多级别,请选择 contains 表的附加的嵌套外部连接。您还可修订此解决方案来返回每一级别上部件的数目的计数。

动态 SQL

虽然静态 SQL 是有用的,但在您编写程序的时候,它要求您知道每个 SQL 语句确切内容。例如,您必须确切说明在任何 WHERE 子句中测试哪些列,以及在任何选择列表中重命名哪些列。

当您编写程序来执行定义良好的任务时,不存在任何问题。但不可事先完善地定义某些程序的数据库任务。特别地,必须响应交互用户的程序可能需要根据用户输入的内容来组合 SQL 语句。

动态 SQL 允许程序在执行期间形成 SQL 语句,因此,用户输入决定该语句的内容。以下列步骤执行此活动:

  1. 程序将 SQL 语句的文本组装为一个字符串,将该字符串存储在程序变量中。
  2. 它执行 PREPARE 语句,请求数据库服务器测试该语句文本并为执行来准备它。
  3. 它使用 EXECUTE 语句来执行该准备好的语句。

这样,基于任何种类的用户输入,程序可构造并然后使用任何 SQL 语句。例如,它可读取一个 SQL 语句的文件并准备和执行每一语句。

DB-Access 是一个您可用来交互地探索 SQL 的实用程序,它是一个动态地构造、准备和执行 SQL 语句的 GBase 8s ESQL/C 程序。例如,DB-Access 允许您使用简单的、交互式的菜单来指定表的行。当您完成时,DB-Access 动态地构建必需的 CREATE TABLE 或 ALTER TABLE 语句并准备和执行它。

准备语句

在形式上,动态 SQL 语句像任何其他写入程序的 SQL 语句一样,除了它不可包含任何主变量的名称之外。

准备好的 SQL 语句有两个限制。首先,如果它是 SELECT 语句,则它不可包括 INTO variable 子句。INTO variable 子句指定将列数据放入其内的主变量,而不允许在准备好的对象的文本中使用主变量。其次,不论主变量的名称通常出现在表达式中的任何位置,都将问号(?)写作 PREPARE 语句中的占位符。仅 PREPARE 语句可指定问号(?)占位符。

您可使用 PREPARE 语句以此形式为执行准备语句。使用 GBase 8s ESQL/C编写下列示例:

EXEC SQL prepare query_2 from
'SELECT * from orders
WHERE customer_num = ? and order_date > ?';

此示例中的两个问号指示当执行该语句时,在那两个位置使用主变量的值。

您可动态地准备几乎任何 SQL 语句。您唯一不可准备的语句就是与动态 SQL 和游标管理直接相关联的语句,诸如 PREPARE 和 OPEN 语句。在您准备 UPDATE 或 DELETE 语句之后,最好测试 SQLWARN 的第五个字段来查看您是否使用了 WHERE 子句(请参阅 SQLWARN 数组)。

准备语句的结果是表示该语句的数据结构。此数据结构与产生它的字符串不一样。在 PREPARE 语句中,您赋予该数据结构一个名称;它是前面示例中的 query_2。使用此名称来执行准备好的 SQL 语句。

PREPARE 语句不将字符串限制于一个语句。它可包含多个用分号分隔的 SQL 语句。下列示例展示用 GBase 8s ESQL/C 编写的相当复杂的事务:

strcpy(big_query, "UPDATE account SET balance = balance + ?
WHERE customer_id = ?; \ UPDATE teller SET balance =
balance + ? WHERE teller_id = ?;");
EXEC SQL PREPARE big1 FROM :big_query;

当执行此语句的列表时,主变量必须为六个占位的问号提供值。虽然设置多语句列表更为复杂,但由于在程序与数据库服务器之间发生更少的交换,因此性能往往更好。

执行准备好的 SQL

在您准备语句之后,您可多次执行它。使用 EXECUTE 语句执行不是 SELECT 语句的那些语句,以及仅返回一行的 SELECT 语句。

下列 GBase 8s ESQL/C 代码准备并执行银行账户的多语句更新:

EXEC SQL BEGIN DECLARE SECTION;
char bigquery[270] = "begin work;";
EXEC SQL END DECLARE SECTION;
stcat ("update account set balance = balance + ? where ", bigquery);
stcat ("acct_number = ?;', bigquery);
stcat ("update teller set balance = balance + ? where ", bigquery);
stcat ("teller_number = ?;', bigquery);
stcat ("update branch set balance = balance + ? where ", bigquery);
stcat ("branch_number = ?;', bigquery);
stcat ("insert into history values(timestamp, values);", bigquery);

EXEC SQL prepare bigq from :bigquery;

EXEC SQL execute bigq using :delta, :acct_number, :delta,
:teller_number, :delta, :branch_number;

EXEC SQL commit work;

EXECUTE 语句的 USING 子句提供主变量的列表,以其值替代准备好的语句中的问号。如果 SELECT(或 EXECUTE FUNCTION)仅返回一行,则您可使用 EXECUTE 的 INTO 子句来指定接收这些值的主变量。

动态主变量

支持动态地分配数据对象的 SQL API 进动态语句更进一步。它们允许您动态地分配接收列数据的主变量。

变量的动态分配使得有可能从程序输入中取到任意的 SELECT 语句,确定它产生多少个值及其数据类型,并分配适当类型的主变量来保存它们。

此功能的关键是 DESCRIBE 语句。它取到准备好的 SQL 语句的名称,并返回关于该语句及其内容的信息。它设置 SQLCODE 来指定语句的类型;即,它开头的动词。如果准备好的语句是 SELECT 语句,则 DESCRIBE 语句还返回关于选择了的输出数据的信息。如果准备好的语句是 INSERT 语句,则 DESCRIBE 语句返回关于输入参数的信息。DESCRIBE 语句返回信息的数据结构是预定义的数据结构,为此用途分配该数据结构并称为系统描述符区域。如果您正在使用 GBase 8s ESQL/C,则可使用系统描述符区域,或作为 sqlda 结构的替代方案。

DESCRIBE 语句返回的或为 SELECT 语句引用的数据结构包括结构的一个数组。每一结构描述为选择类表中一个项目的数据。程序可检测该数组并发现包括十进制值、某长度的字符值以及整数的一行数据。

利用此信息,程序可分配保存接收了的数据的内存,并为要使用的数据库服务器将必要的指针放置在该数据结构中。

释放准备好的语句

准备好的 SQL 语句占据内存中的空间。对于某些数据库服务器,它可消耗数据库服务器拥有的空间以及属于该程序的空间。当程序终止时,释放此空间,但通常您应在使用完此空间时就释放它。

您可使用 FREE 语句来释放此空间。FREE 语句采用语句的名称,或为语句名称声明了的游标的名称,并释放分配给准备好的语句的空间。如果在该语句上定义多个游标,则释放该语句不会释放游标。

快速执行

对于不需要游标或主变量的简单语句,您可将 PREPARE、EXECUTE 和 FREE 语句组合到单个操作内。下列示例展示 EXECUTE IMMEDIATE 语句如何获取字符串、准备它、执行它,并释放一个操作中的存储:

EXEC SQL execute immediate 'drop index my_temp_index';

此能力使得编写简单 SQL 语句更轻松。然而,由于不允许使用 USING 子句,EXECUTE IMMEDIATE 语句不可用于 SELECT 语句。

嵌入数据定义语句

数据定义语句是创建数据库和修改表的定义的 SQL 语句,通常将这些语句放在程序内。原因是很少执行它们。数据库只创建一次,但会多次查询和更新它。

使用 DB-Access,一般都是交互地完成数据库及其表的创建。还可从语句的文件运行这些工具,因此可使用操作系统命令来完成数据库创建。在 GBase 8s SQL 指南:语法中描述数据定义语句。

授予和撤销应用程序中的权限

反复地执行与数据定义相关的一个任务:授予和撤销权限。由于必须频繁地授予和撤销权限,有可能是由不熟悉 SQL 的用户操作,因此一种策略是将 GRANT 和 REVOKE 语句打包在程序中,来提供给他们更简单、更方便的用户接口。

GRANT 和 REVOKE 语句特别适合于动态 SQL。每一语句采用下列参数:

  • 一个或多个权限的列表
  • 表名称
  • 用户的名称

您可能至少需要基于程序输入(来自于用户、命令行参数或文件)提供这些值的一部分,但都不可以主变量的形式提供。这些语句的语法不允许在任何位置使用主变量。

一个替代方案是将语句的各个部分组合到字符串内,并准备和执行组合好的语句。程序输入可作为字符串合并到准备好的语句内。

下列 GBase 8s ESQL/C 函数从参数组合 GRANT 语句,然后准备和执行它:

char priv_to_grant[100];
char table_name[20];
char user_id[20];

table_grant(priv_to_grant, table_name, user_id)
char *priv_to_grant;
char *table_name;
char *user_id;
{
EXEC SQL BEGIN DECLARE SECTION;
char grant_stmt[200];
EXEC SQL END DECLARE SECTION;

sprintf(grant_stmt, " GRANT %s ON %s TO %s",
priv_to_grant, table_name, user_id);
PREPARE the_grant FROM :grant_stmt;
if(SQLCODE == 0)
EXEC SQL EXECUTE the_grant;
else
printf("Sorry, got error # %d attempting %s",
SQLCODE, grant_stmt);

EXEC SQL FREE the_grant;
}

下列示例展示的该函数的打开语句指定它的名称及其三个参数。这三个参数指定要授予的权限、对其授予权限的表的名称,以及要接收它们的用户的 ID。

table_grant(priv_to_grant, table_name, user_id)
char *priv_to_grant;
char *table_name;
char *user_id;

该函数使用下例中的语句来定义本地变量 grant_stmt,使用其来组合并保存 GRANT 语句:

EXEC SQL BEGIN DECLARE SECTION;
char grant_stmt[200];
EXEC SQL END DECLARE SECTION;

如下例所示,通过将该语句的常量部分与函数参数连起来,创建该 GRANT 语句:

sprintf(grant_stmt, " GRANT %s ON %s TO %s",priv_to_grant,
table_name, user_id);

此语句将下列六个字符串连接起来:

  • 'GRANT'
  • 指定要授予的权限的参数
  • 'ON'
  • 指定表名称的参数
  • 'TO'
  • 指定用户的参数

结果是部分由程序输入组成的完整的 GRANT 语句。PREPARE 语句将组合的语句文本传给数据库服务器进行解析。

如果数据库服务器跟在 PREPARE 语句之后在 SQLCODE 中返回错误代码,则该函数显示错误消息。如果数据库服务器认可该语句的形式,则它设置零返回值。此活动并不保证正确地执行该语句;它仅意味着该语句的语法正确。它可能引用不存在的表或某些种类仅可在执行期间才能检测到的错误。该示例的下列部分在执行之前检查 the_grant 是否准备成功了:

if(SQLCODE == 0)
EXEC SQL EXECUTE the_grant;
else
printf("Sorry, got error # %d attempting %s", SQLCODE, grant_stmt);

如果准备成功,则 SQLCODE = = 0,下一步执行准备好的语句。

指定角色

或者,DBA 可使用 CREATE ROLE 语句定义一个角色,并使用 GRANT 和 REVOKE 语句来将角色分配给用户或取消,以及授予和撤销角色权限。例如:

GRANT engineer TO nmartin;

需要 SET ROLE 语句来激活非缺省的角色。要获取关于角色和权限的更多信息,请参阅 访问管理策略 和 对数据库级对其对象的权限。要获取关于这些语句的语法的更多信息,请参阅《GBase 8s SQL 指南:语法》。

总结

可将 SQL 语句写入程序内,如同它们是不同的编程语言的语句那样。可在 WHERE 子句中使用程序变量,可将来自数据库的数据访存到它们之内。预处理器将 SQL 代码翻译为过程调用和数据结构。

编写不返回数据的语句,或仅返回一行数据的查询,就像该语言的普通命令语句一样。可返回多行的查询与表示当前数据行的游标相关联。通过游标,程序可根据需要访存数据的每一行。

将静态 SQL 语句写入程序的文本内。然而,程序在它运行时动态地形成新的 SQL 语句,并执行它们。在最先进的情况下,程序可获得关于查询返回的列的数目和类型,并动态地分配内存空间来保存它们。