实现游标
为 什 么 要 用到 游 标
---- 在某 些PowerBuilder 应 用 程 序 的 开 发 中, 您 可 能 根 本 用 不 到 游标 这 样 一 个 对 象。 因 为 在 其 它 工 具 开 发 中 很 多 需 用 游标 实 现 的 工 作, 在PowerBuilder 中 却 已 有DataWin-dow 来 代 劳 了。 事实 上,DataWindow 不 仅 可 以 替 代 游 标 进 行 从 后 台 数 据 库 查 询多 条 记 录 的 复 杂 操 作, 而 且 还 远 不 止 这 些。 但 是 同DataWindow 和DataStore 相 比, 游 标 也 有 其 自 身 的 优 点, 比 如 系 统 资 源 占用 少, 操 作 灵 活, 可 根 据 需 要 定 义 变 量 类 型 如 全 局、 实例 或 局 部 类 型 和 访 问 类 型 如 私 有 或 公 共 等 。
游 标 的 操 作
---- 使用 游 标 有 四 种 基 本 的 步 骤: 声 明 游 标、 打 开 游 标、 提 取数 据、 关 闭 游 标。
----
声 明 游 标
---- 象使 用 其 它 类 型 的 变 量 一 样, 使 用 一 个 游 标 之 前, 首 先 应当 声 明 它。 游 标 的 声 明 包 括 两 个 部 分: 游 标 的 名 称; 这个 游 标 所 用 到 的SQL 语 句。 如 要 声 明 一 个 叫 作Cus-tomerCursor 的 游 标 用 以 查 询 地 址 在 北 京 的 客 户 的 姓 名、 帐 号 及 其余 额, 您 可 以 编 写 如 下 代 码:
----DECLARE CustomerCursor CURSOR FOR
----SELECT acct_no,name,balance
----FROM customer
----WHERE province=" 北 京";
---- 在游 标 的 声 明 中 有 一 点 值 得 注 意 的 是, 如 同 其 它 变 量 的声 明 一 样, 声 明 游 标 的 这 一 段 代 码 行 是 不 执 行 的, 您 不能 将debug 时 的 断 点 设 在 这 一 代 码 行 上, 也 不 能 用IF...END IF 语 句 来 声 明 两 个 同 名 的 游 标, 如 下 列 的 代 码 就 是 错 误的。
----IF Is_prov=" 北 京"THEN
----DECLARE CustomerCursor CURSOR FOR
----SELECT acct_no,name,balance
----FROM customer
----WHERE province=" 北 京";
----ELSE
----DECLARE CustomerCursor CURSOR FOR
----SELECT acct_no,name,balance
----FROM customer
----WHERE province〈〉" 北 京";
----END IF
----
打 开 游 标
---- 声明 了 游 标 后 在 作 其 它 操 作 之 前, 必 须 打 开 它。 打 开 游标 是 执 行 与 其 相 关 的 一 段SQL 语 句 , 例 如 打 开 上 例 声 明的 一 个 游 标, 我 们 只 需 键 入:
----OPEN CustomerCursor;
---- 由于 打 开 游 标 是 对 数 据 库 进 行 一 些SQL SELECT 的 操 作, 它 将 耗费 一 段 时 间, 主 要 取 决 于 您 使 用 的 系 统 性 能 和 这 条 语句 的 复 杂 程 度。 如 果 执 行 的 时 间 较 长, 可 以 考 虑 将 屏幕 上 显 示 的 鼠 标 改 为hourglass。
---- 提取 数 据
---- 当用OPEN 语 句 打 开 了 游 标 并 在 数 据 库 中 执 行 了 查 询 后, 您不 能 立 即 利 用 在 查 询 结 果 集 中 的 数 据。 您 必 须 用FETCH 语 句 来 取 得 数 据。 一 条FETCH 语 句 一 次 可 以 将 一 条 记 录放 入 程 序 员 指 定 的 变 量 中。 事 实 上,FETCH 语 句 是 游 标 使用 的 核 心。 在DataWindow 和DataStore 中, 执 行 了R etrieve() 函 数 以 后, 查 询 的 所 有 结 果 全 部 可 以 得 到; 而 使 用 游 标, 我 们 只 能逐 条 记 录 地 得 到 查 询 结 果。
---- 已经 声 明 并 打 开 一 个 游 标 后, 我 们 就 可 以 将 数 据 放 入 任意 的 变 量 中。 在FETCH 语 句 中 您 可 以 指 定 游 标 的 名 称 和目 标 变 量 的 名 称。 如 下 例:
----FETCH CustmerCur-sor
----INTO:ls_acct_no,
----:ls_name,
----:ll_balance;
---- 从语 法 上 讲, 上 面 所 述 的 就 是 一 条 合 法 的 取 数 据 的 语 句, 但 是 一 般 我 们 使 用 游 标 却 还 应 当 包 括 其 它 的 部 分。 正如 我 们 前 面 所 谈 到 的, 游 标 只 能 一 次 从 后 台 数 据 库 中取 一 条 记 录, 而 在 多 数 情 况 下, 我 们 所 想 要 作 的 是 在 数据 库 中 从 第 一 条 记 录 开 始 提 取, 一 直 到 结 束。 所 以 我们 一 般 要 将 游 标 提 取 数 据 的 语 句 放 在 一 个 循 环 体 内, 直 至 将 结 果 集 中 的 全 部 数 据 提 取 后, 跳 出 循 环 圈 。 通过 检 测SQLCA.SQL-CODE 的 值, 可 以 得 知 最 后 一 条FETCH 语 句 是 否成 功。 一 般, 当SQLCODE 值 为0 时 表 明 一 切 正 常,100 表 示 已 经取 到 了 结 果 集 的 末 尾, 而 其 它 值 均 表 明 操 作 出 了 问 题, 这 样 我 们 可 以 编 写 以 下 的 代 码:
----lb_continue=True
----ll_total=0
----DO WHILE lb_continue
----FETCH CustomerCur-sor
----INTO:ls_acct_no,
----:ls_name,
----:ll_balance;
----If sqlca.sqlcode=0 Then
----ll_total+=ll_balance
----Else
----lb_continue=False
----End If
----LOOP
---- 循环 体 的 结 构 有 多 种, 这 里 提 到 的 是 最 常 见 的 一 种。 也有 的 程 序 员 喜 爱 将 一 条FETCH 语 句 放 在 循 环 体 的 前 面, 循环 体 内 再 放 置 另 外 一 条FETCH 语 句, 并 检 测SQLCA.SQLCODE 是 否 为100。但 是 这 样 做, 维 护 时 需 同 时 修 改 两 条FETCH 语 句, 稍 麻 烦 了些。
----
关 闭 游 标
---- 在游 标 操 作 的 最 后 请 不 要 忘 记 关 闭 游 标, 这 是 一 个 好 的编 程 习 惯, 以 使 系 统 释 放 游 标 占 用 的 资 源。 关 闭 游 标的 语 句 很 简 单:
----CLOSE CustomerCursor;
----
使 用Where 子 句 子
---- 我们 可 以 动 态 地 定 义 游 标 中 的Where 子 句 的 参 数, 例 如 在 本例 中 我 们 是 直 接 定 义 了 查 询 省 份 是 北 京 的 记 录, 但 也许 在 应 用 中 我 们 要 使 用 一 个 下 拉 式 列 表 框, 由 用 户 来选 择 要 查 询 的 省 份 , 我 们 该 怎 样 做 呢?
---- 我们 在 前 面 曾 经 提 到 过,DECLARE 语 句 的 作 用 只 是 定 义 一 个游 标, 在OPEN 语 句 中 这 个 游 标 才 会 真 正 地 被 执 行。 了 解了 这 些, 我 们 就 可 以 很 方 便 地 实 现 这 样 的 功 能, 在DECLARE 的Where 子 句 中 加 入 变 量 作 参 数, 如 下 所 示:
----DECLARE CustomerCursor CURSOR FOR
----SELCECT acct_no,name,balance
----FROM customer
----WHERE province=:ls_province;
---- ∥ 定 义ls_province 的 值
----OPEN CustomerCursor;
----
游 标 的 类 型
---- 同其 它 变 量 一 样, 我 们 也 可 以 定 义 游 标 的 访 问 类 型: 全局、 共 享、 实 例 或 局 部, 游 标 变 量 的 命 名 规 范 建 议 也 同其 它 变 量 一 样。
游 标 的 高 级技 巧
---- 尽管 目 前 基 于SQL 语 句 的 后 台 数 据 库 所 支 持 的 语 言 都 大 致相 当, 但 对 游 标 的 支 持 却 有 着 一 些 差 异, 例 如 对 滚 动 游标 支 持。 所 谓 滚 动 游 标, 就 是 程 序 员 可 以 指 定 游 标 向前 后 任 意 一 个 方 向 滚 动。 如 在Informix 中, 您 甚 至 还 可 以将 游 标 滚 向 结 果 集 开 头 或 末 尾, 使 用 的 语 句 分 别 是FET CH FIRST,FETCH LAST、FETCH PRIOR 和FETCH NEXT。 当 程 序 员 用FETCH 语 句, 其 缺省 是 指FET CH NEXT。 由 于 滚 动 是 在 数 据 库 后 台 实 现 的, 所 以滚 动 游 标 为 用 户 编 程 提 供 了 极 大 的 方 便。
---- 对游 标 支 持 的 另 一 个 不 同 是 可 修 改 游 标。 上 述 游 标 的 使用 都 是 指 只 读 游 标, 而 象Oracle 、Sybase 等 数 据 库 却 另 外 支持 可 作 修 改 的 游 标。 使 用 这 样 的 数 据 库, 您 可 以 修 改或 删 除 当 前 游 标 所 在 的 行。 例 如 修 改 当 前 游 标 所 在 行的 用 户 的 余 额, 我 们 可 以 如 下 操 作:
----UPDATE customer
----SET balance=1000
----WHERE CURRENT of customerCursor;
---- 删除 当 前 行 的 操 作 如 下:
----DELETE FROM Customer
----WHERE CURRENT OF CustomerCursor;
---- 但是 如 果 您 当 前 使 用 的 数 据 库 是Sybase, 您 需 要 修 改 数 据 库的 参 数, 将 游 标 可 修 改 的 值 定 为1, 才 能 执 行 上 述 操 作。这 一 赋 值 在 连 接 数 据 库 的 前 后 进 行 均 可。
----SQLCA.DBParm="Cursor Update=1"
---- 另外 一 个 内 容 是 动 态 游 标, 也 就 是 说 您 可 以 运 行 过 程 中动 态 地 形 成 游 标 的SELECT 语 句。 这 同 在PowerBuilder 中 动 态 地使 用 嵌 入 式SQL 一 样, 需 要 用 到DynamicStagin-gArea 等 数 据 类 型, 这 已 超 出 了 本 节 的 范 围。
游标教程参考 http://www.pgsqldb.org/pgsqldoc-8.0c/plpgsql-cursors.html
Oracle在使用SELECT语句时总是需要游标,不管从数据库中请求多少行。在Microsoft SQL Server,SELECT语句并不把在返回客户的行上附加游标作为缺省的结果集合。这是一种返回数据给客户应用程序的有效的方法。
SQL Server为游标函数提供了两种接口。当在Transact-SQL批处理或者存储过程中使用游标的时候,SQL语句可用来声明、打开、和从游标中抽取,就像定位更新和删除一样。当使用来自DB-Library、ODBC、或者OLEDB程序的游标时,SQL Server显式的调用内建的服务器函数来更有效的处理游标。
当从Oracle输入一个PL/SQL过程时,首先判断是否需要在Transact-SQL中采用游标来实现同样的功能。如果游标仅仅返回一组行给客户程序,就使用非游标的SELECT语句来返回缺省的结果集合。如果游标用来从行中一次取得一个数据给本地过程变量,你就必须在Transact-SQL中使用游标。
语法
下表显示了使用游标的语法。
操作 |
Oracle |
Microsoft SQL Server |
声明一个游标 |
CURSOR cursor_name [(cursor_parameter(s))] |
DECLARE cursor_name CURSOR |
打开一个游标 |
OPEN cursor_name [(cursor_parameter(s))]; |
OPEN cursor_name |
从游标中提取(Fetching) |
FETCH cursor_name INTO variable(s) |
FETCH [[NEXT | PRIOR | FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}] |
更新提取行 |
UPDATE table_name |
UPDATE table_name |
删除提取行 |
DELETE FROM table_name |
DELETE FROM table_name |
关闭游标 |
CLOSE cursor_name; |
CLOSE cursor_name |
清除游标数据结构 |
N/A |
DEALLOCATE cursor_name |
声明一个游标
尽管Transact-SQL DECLARE CURSOR语句不支持游标参数的使用,但它确实支持本地变量。当游标打开的时候,它就使用这些本地变量的值。Microsoft SQL Server在其DECLARE CURSOR中提供了许多附加的功能。
INSENSITIVE选项用来定义一个创建数据的临时拷贝以被游标使用的游标。游标的所有请求都由这个临时表来应答。因此,对原表的修改不会反映到那些由fetch返回的用于该游标的数据上。这种类型的游标访问的数据是不能被修改的。
应用程序可以请求一个游标类型然后执行一个不被所请求的服务器游标类型支持的Transact-SQL语句。SQL Server返回一个错误,指出该游标类型被改变了,或者给出一组参数,隐式的转换游标。欲取得一个触发SQL Server 7.0隐式的把游标从一种类型转换为另一种类型的参数的完整列表,请参阅SQL Server联机手册。
SCROLL选项允许除了前向的抽取以外,向后的、绝对的和相对的数据抽取。一个滚动游标使用一种键集合的游标模型,在该模型中,任何用户提交的对表的删除和更新都将影响后来的数据抽取。只有在游标没有用INSENSITIVE选项声明时,上面的特性才起作用。
如果选择了READ ONLY选项,对游标中的行的更新就被禁止。该选项将覆盖游标的缺省选项——允许更新。
UPDATE [OF column_list]语句用来在游标中定义一个可更新的列。如果提供了[OF column_list],那么仅仅是那些列出的列可以被修改。如果没有指定任何列。则所有的列都是可以更新的,除非游标被定义为READ ONLY。
重要的是,注意到一个SQL Server游标的名字范围就是连接自己。这和本地变量的名字范围是不同的。不能声明一个与同一个用户连接上的已有的游标相同名字的游标,除非第一个游标被释放。
打开一个游标
Transact-SQL不支持向一个打开的游标传递参数,这一点和PL/SQL是不一样的。当一个Transact-SQL游标被打开以后,结果集的成员和顺序就固定下来了。其它用户提交的对原表的游标的更新和删除将反映到对所有未加INSENSITIVE选项定义的游标的数据抽取上。对一个INSENSITIVE游标,将生成一个临时表。
抽取数据
Oracle游标只能向前移动——没有向后或者相对滚动的能力。SQL Server游标可以向前或者向后滚动,具体怎么滚动,要由下表给出的数据抽取选项来决定。只有在游标是用SCROLL选项声明的前提下,这些选项才能使用。
卷动选项 |
描述 |
NEXT |
如果这是对游标的第一次提取,则返回结果集合的第一行;否则,在结果结合内移动游标到下一行。NEXT是在结果集合中移动的基本方法。NEXT是缺省的游标提取(fetch)。 |
PRIOR |
返回结果集合的前一行。 |
FIRST |
把游标移动到结果集合的第一行,同时返回第一行。 |
LAST |
把游标移动到结果集合的最后一行,同时返回最后一行。 |
ABSOLUTE n |
返回结果集合的第n行。如果n为负数,则返回倒数第n行 |
RELATIVE n |
返回当前提取行后的第n行,如果n是负数,则返回从游标相对位置起的倒数第n行。 |
Transact-SQL的FETCH语句不需要INTO子句。如果没有指定返回变量,行就自动作为一个单行结果集合返回给客户。但是,如果你的过程必须把行给客户,一个不带游标的SELECT语句更有效一些。
在每一个FETCH后面,@@FETCH_STATUS函数被更新。这和在PL/SQL中使用CURSOR_NAME%FOUND和CURSOR_NAME%NOTFOUND变量是相似的。@@FETCH_STATUS函数在每一次成功的数据抽取以后被设定为0。如果数据抽取试图读取一个超过游标末尾的数据,则返回一个为-1的值。如果请求的行在游标打开以后从表上被删除了,@@FETCH_STATUS函数就返回一个为-2的值。只有游标是用SCROLL选项定义的情况下,才会返回-2值。在每一次数据抽取之后都必须检查该变量,以确保数据的有效性。
SQL Server不支持Oracle的游标FOR循环语法。
CURRENT OF子句
更新和删除的CURRENT OF子句语法和函数在PL/SQL和Transact-SQL中是一样的。在给定游标中,在当前行上执行定位的UPDATE和DELETE。
关闭一个游标
Transact-SQL的CLOSE CURSOR语句关闭游标,但是保留数据结构以备重新打开。PL/SQL 的CLOSE CURSOR语句关闭并且释放所有的数据结构。
Transact-SQL需要用DEALLOCATE CURSOR语句来清除游标数据结构。DEALLOCATE CURSOR语句同CLOSE CURSOR是不一样的,后者保留数据结构以备重新打开。DEALLOCATE CURSOR释放所有与游标相关的数据结构并且清除游标的定义。
游标示例
下面的例子显示了在PL/SQL和Transact-SQL等价的游标语句。
Oracle |
Microsoft SQL |
DECLARE |
DECLARE |
CURSOR CUR1 IS |
DECLARE curl CURSOR FOR |