pbai 发表于 2026-8-21 10:21:34

PowerBuilder 嵌入式 SQL 实战:CONNECT 连接、增删改查与游标取数

PowerBuilder 嵌入式 SQL 实战:CONNECT 连接、增删改查与游标取数


阅读说明
1. 适用版本:PB 9 及以上(本文按 PB 12.5 验证;纯经典 PowerBuilder,不依赖 PBIDEA 运行库)
2. 支持数据库:MSSQL(SQL Server 2000+) / Oracle / MySQL 等 PB 支持的数据库均可(示例 SQL 均为 ANSI 通用写法,不含任何方言特性)
3. 操作系统与环境要求:Windows 7+,已安装 PowerBuilder 开发环境;运行前需为所用数据库配置好 PB 数据库接口(如 MSSQL 的 OLE DB/ODBC 或专用接口),并在应用初始化时给 SQLCA 赋值
4. 难度系数:★☆☆☆☆
5. 其它阅读说明:前置知识为 PowerScript 基本语法与变量声明;示例需先在数据库中建好测试表 employee(建表语句见文末)


一、嵌入式 SQL 是什么(先搞懂原理)

PowerBuilder 里操作数据库有两条路:一条是 DataWindow / DataStore(可视化组件,自动生成 SQL、自动管理缓冲区),另一条就是本文要讲的嵌入式 SQL——把 SQL 语句直接写在 PowerScript 脚本里,语句结束用分号,紧跟 USING SQLCA; 指定在哪个事务对象上执行。

嵌入式 SQL 的关键在于 SQLCA 事务对象。它是 PB 内置的全局 transaction 对象,所有嵌入式 SQL 的连接状态、执行结果、错误信息都挂在这个对象上。你要记得的主要是它这几个属性:


属性含义常见值
SQLCA.SQLCode上一条 SQL 的执行结果码0=成功;100=未找到/取完;-1=出错
SQLCA.SQLDBCode数据库返回的错误码(DBMS 原生码)如 MSSQL 的 2627 是主键冲突
SQLCA.SQLErrText数据库返回的错误文本可直接弹给用户看
SQLCA.SQLNRows上一条 INSERT/UPDATE/DELETE 影响的行数0 表示没有行被更新


嵌入式 SQL 与动态 SQL(Dynamic SQL)的区别:嵌入式 SQL 在编译期就确定语句结构,变量用冒号绑定;动态 SQL 是运行时才拼出完整语句(EXECUTE IMMEDIATE / PREPARE+EXECUTE),适合表名、列名都不确定的场景。日常固定结构的增删改查,用嵌入式 SQL 就够了。

二、连接与断开:CONNECT / DISCONNECT

连接前先要给 SQLCA 赋值(数据库名、用户名、密码、数据库接口等),通常放在应用对象的 open 事件里,用 ProfileString 从 .ini 读,或直接写死。例如:SQLCA.DBMS = 'MSSQL'、SQLCA.Database = 'demo'、SQLCA.UserID = 'sa' 等。赋好值后执行 CONNECT USING SQLCA;,必须检查 SQLCA.SQLCode,不为 0 就是连不上,直接提示错误返回;程序退出前要 DISCONNECT USING SQLCA; 释放连接。

第一个示例把「连接 + 单行查询」串起来:


前置:① 应用 open 事件里已给 SQLCA 赋值(数据库接口/库名/账号密码);② 数据库里有 employee 表,emp_id=1001 的记录存在。
步骤:1) 打开窗口 w_demo;2) 在按钮 cb_query 的 clicked 事件贴入下方代码;3) 运行并点击按钮,弹窗显示查询结果。



// ===== 1. 连接数据库(若已连接可跳过)=====
CONNECT USING SQLCA;
if SQLCA.SQLCode <> 0 then
    MessageBox('连接失败', string(SQLCA.SQLDBCode) + ':' + SQLCA.SQLErrText)
    return
end if

// ===== 2. 示例输入:要查询的员工编号 =====
long ll_emp_id
ll_emp_id = 1001

// ===== 3. 单行查询:SELECT INTO =====
// 结果变量前必须加冒号 :(绑定变量语法);USING SQLCA 指定事务对象
string ls_name
decimal ldc_salary
SELECT emp_name, salary
    INTO :ls_name, :ldc_salary
    FROM employee
    WHERE emp_id = :ll_emp_id
    USING SQLCA;

// ===== 4. 按 SQLCode 分三种情况处理 =====
if SQLCA.SQLCode = 100 then
    MessageBox('查询结果', '未找到员工 ' + string(ll_emp_id))
elseif SQLCA.SQLCode < 0 then
    MessageBox('查询失败', string(SQLCA.SQLDBCode) + ':' + SQLCA.SQLErrText)
else
    MessageBox('查询结果', '员工:' + ls_name + ',工资:' + string(ldc_salary))
end if


注意:SELECT INTO 只能取一行。如果查询结果多于一行,PB 会报错(SQLCode=-1)。要取多行,用第五节讲到的游标。

三、增删改:INSERT / UPDATE / DELETE

增删改的套路完全一致:写 SQL、用冒号绑定变量(不要拼字符串,既防 SQL 注入又省去引号转义)、执行后检查 SQLCA.SQLCode,成功就 COMMIT 提交、失败就 ROLLBACK 回滚。影响行数看 SQLCA.SQLNRows。


前置:已连接数据库(沿用示例一);employee 表存在。
步骤:在按钮 cb_write 的 clicked 事件贴入下方代码;先插一条、再涨薪 10%、最后删除,每步弹窗看影响行数。



// ===== 示例输入:新员工信息(前段声明并直接赋值)=====
long ll_new_id
ll_new_id = 2001
string ls_new_name
ls_new_name = '张三'
decimal ldc_new_salary
ldc_new_salary = 8000

// ===== INSERT:插入一行,用绑定变量 =====
INSERT INTO employee(emp_id, emp_name, salary)
    VALUES (:ll_new_id, :ls_new_name, :ldc_new_salary)
    USING SQLCA;
if SQLCA.SQLCode = 0 then
    COMMIT USING SQLCA;
    MessageBox('插入成功', '影响行数:' + string(SQLCA.SQLNRows))
else
    ROLLBACK USING SQLCA;
    MessageBox('插入失败', SQLCA.SQLErrText)
end if

// ===== UPDATE:给该员工涨薪 10% =====
UPDATE employee SET salary = salary * 1.1
    WHERE emp_id = :ll_new_id
    USING SQLCA;
if SQLCA.SQLCode = 0 then
    COMMIT USING SQLCA;
    MessageBox('更新成功', '影响行数:' + string(SQLCA.SQLNRows))
else
    ROLLBACK USING SQLCA;
    MessageBox('更新失败', SQLCA.SQLErrText)
end if

// ===== DELETE:删除该员工 =====
DELETE FROM employee
    WHERE emp_id = :ll_new_id
    USING SQLCA;
if SQLCA.SQLCode = 0 then
    COMMIT USING SQLCA;
    MessageBox('删除成功', '影响行数:' + string(SQLCA.SQLNRows))
else
    ROLLBACK USING SQLCA;
    MessageBox('删除失败', SQLCA.SQLErrText)
end if


四、多行取数:游标 DECLARE / OPEN / FETCH / CLOSE

一次要取很多行时用游标,四步:DECLARE 声明 → OPEN 打开 → FETCH 循环取行 → CLOSE 关闭。一个最容易踩的坑:DECLARE 语句必须放在脚本里所有可执行语句之前(变量声明不算可执行语句,可以放前面),否则编译不过。


前置:已连接数据库;employee 表有多条 salary>5000 的记录。
步骤:在按钮 cb_cursor 的 clicked 事件贴入下方代码;运行后弹窗列出全部高薪员工。



// ===== 示例输入:工资筛选下限 =====
long ll_min_salary
ll_min_salary = 5000

// ===== 1. DECLARE:声明游标(必须放在所有可执行语句之前!)=====
DECLARE cur_emp CURSOR FOR
    SELECT emp_id, emp_name FROM employee
    WHERE salary > :ll_min_salary
    USING SQLCA;

// ===== 2. OPEN:打开游标 =====
OPEN cur_emp;
if SQLCA.SQLCode <> 0 then
    MessageBox('打开游标失败', SQLCA.SQLErrText)
    return
end if

// ===== 3. FETCH:循环取行,SQLCode=100 表示取完 =====
long ll_id
string ls_name
long ll_count
ll_count = 0
string ls_out
ls_out = '高薪员工清单:' + '~r~n'

FETCH cur_emp INTO :ll_id, :ls_name;
DO WHILE SQLCA.SQLCode = 0
    ll_count = ll_count + 1
    ls_out = ls_out + string(ll_id) + ' ' + ls_name + '~r~n'
    FETCH cur_emp INTO :ll_id, :ls_name;
LOOP

// ===== 4. CLOSE:关闭游标释放资源 =====
CLOSE cur_emp;

MessageBox('游标取数', ls_out + '(共 ' + string(ll_count) + ' 条)')


五、错误处理与事务要点(把坑提前排掉)

把上面几个示例的共性总结成一张自查表,写嵌入式 SQL 时逐条对照:


[*]每次执行后立刻查 SQLCA.SQLCode:0 成功、100 无记录(SELECT INTO 未命中 / FETCH 已取完)、-1 出错。漏查等于裸奔。
[*]绑定变量一律加冒号 :var:INTO :ls_name、WHERE emp_id = :ll_emp_id。漏了冒号编译就报错,写对了就不用拼字符串。
[*]SELECT INTO 只取一行:多行结果会报错;需要多行用游标,或直接用 DataWindow。
[*]DECLARE 游标必须在可执行语句之前:放在函数/事件开头,别写进 IF 块或循环里。
[*]事务要么提交要么回滚:增删改成功 COMMIT USING SQLCA;、失败 ROLLBACK USING SQLCA;,不然数据一直挂起(锁表、脏读都跟它有关)。
[*]程序退出前 DISCONNECT USING SQLCA;:尤其连接池场景,不释放会占着连接。
[*]游标取完必须 CLOSE:不关会占数据库资源,循环里大量 OPEN 不 CLOSE 最终拖垮会话。


六、嵌入式 SQL vs DataWindow、动态 SQL(怎么选)


场景选哪个理由
界面绑数据、增删改带校验DataWindow / DataStore自动生成 SQL、缓冲区、编辑校验一整套
后台批处理、无界面脚本、简单单表操作嵌入式 SQL轻、直白、无组件依赖
临时拼一句 SQL(表名/条件不固定)动态 SQL(EXECUTE IMMEDIATE)嵌入式 SQL 无法处理动态表名
大批量逐行处理游标(或 DataStore + GetItemXxx)游标逐行可控,DW 批量更快


一句话:界面交互用 DataWindow,脚本逻辑用嵌入式 SQL,结构不确定才上动态 SQL。本文示例的固定结构增删改查,就是嵌入式 SQL 的典型适用区。

七、建表 SQL 与小结

先在数据库客户端执行下面这句建表语句(MSSQL 语法;Oracle/MySQL 把 decimal(10,2) 换成各自等价类型即可):

CREATE TABLE employee (emp_id int PRIMARY KEY, emp_name varchar(50), salary decimal(10,2));

小结一下本文核心:嵌入式 SQL = 脚本里直接写 SQL + USING SQLCA 指定事务对象 + 冒号绑定变量 + 按 SQLCA.SQLCode 判断结果 + COMMIT/ROLLBACK 管事务;单行查询用 SELECT INTO,多行取数用 DECLARE/OPEN/FETCH/CLOSE 四步游标。把这套骨架记住,任何固定结构的增删改查都能半小时内写出来。
页: [1]
查看完整版本: PowerBuilder 嵌入式 SQL 实战:CONNECT 连接、增删改查与游标取数

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com