马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
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 四步游标。把这套骨架记住,任何固定结构的增删改查都能半小时内写出来。 |