马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?站点注册
×
PBIDEA:uo_database 连接 SQL Server 2022 完整实战(连接 / 增删改查 / 存储过程 / 建库 SQL)
阅读说明
1. 适用版本:PB 12.5 及以上(含 PBIDEA 1.x,uo_database / uo_recordset 位于 sql.pbl)
2. 支持数据库:SQL Server 2022(本文实测对象;连接写法同样适用于 SQL Server 2012–2019,客户端驱动按文末表格选择)
3. 操作系统与环境要求:Windows 7+(已装 PBIDEA 运行库,含 PbIdea.dll);SQL Server 2022 服务器仅支持 64 位系统(Win10 1607+ / Win11 / Server 2016+);客户端需安装 ODBC Driver 18 for SQL Server(或 OLE DB Driver 19)
4. 难度系数:★★★☆☆(三星:需要会装 SQL Server、会用 SSMS 执行建库脚本;纯 PB 代码部分难度两星)
5. 其它阅读说明:建议先读 Day7《PBIDEA:用 uo_database 与 uo_database_pool 做多库连接和连接池复用》了解对象基本用法;示例库/表/存储过程直接用本文第二节脚本在 SSMS 里执行即可
经典 PowerBuilder 连 SQL Server,很多人还停留在 SQLCA + DBMS='MSS' + SQL Native Client 那套老配置。到了 SQL Server 2022,老驱动已经带不动了:微软早已停止 SQL Native Client(SQLNCLI11)的功能更新,官方文档明确 SQLNCLI11 不支持 SQL Server 2022——你会在连接时报 08001 SQL Server Native Client 11.0 不支持连接到 SQL Server 2022。
PBIDEA 的 uo_database(继承自 transaction,可 create 任意多个)配合新驱动,正好把这件事做干净。本文按「环境搭建 → 建库 SQL → 连接方式 → 增删改查 → 存储过程 → 常见坑」的顺序,给出全部可直接复制运行的代码。
一、环境搭建:服务器、客户端驱动、DSN
1.1 安装 SQL Server 2022(服务器端)
个人学习/开发用 Express(免费版) 或 Developer(开发版,免费但不可商用) 即可,不用买许可。
| 软件 | 下载地址 | 说明 | | SQL Server 2022 下载中心 | https://www.microsoft.com/sql-server/sql-server-downloads | 官方总入口,选 Express 或 Developer | | SQL Server 2022 Express 直链 | https://www.microsoft.com/en-us/download/details.aspx?id=104781 | 下载 SQL2022-SSEI-Expr.exe(约 5–6MB 的安装引导器) | | SSMS(图形管理工具) | https://learn.microsoft.com/zh-cn/sql/ssms/download-sql-server-management-studio-ssms | 强烈建议装,建库/建存过/查数据全靠它 | | ODBC Driver 18 for SQL Server | https://learn.microsoft.com/zh-cn/sql/connect/odbc/download-odbc-driver-for-sql-server | 客户端必装(PB 侧连接的关键驱动,见 1.2) | | OLE DB Driver 19 for SQL Server | https://learn.microsoft.com/zh-cn/sql/connect/oledb/download-oledb-driver-for-sql-server | 走 OLE DB 时的备选驱动 |
安装要点(Express 为例):
- 双击 SQL2022-SSEI-Expr.exe,选 Custom(自订)——Basic 是纯默认安装,Custom 才能设认证模式与实例名。
- 功能勾选:Database Engine Services 必选;Full-Text Search、LocalDB 可选。
- 实例配置:Express 默认实例名 SQLEXPRESS,连接地址为 localhost\SQLEXPRESS(本机)或 计算机名\SQLEXPRESS(远程)。保持默认即可。
- 身份验证模式:选「混合模式(Mixed Mode)」,给 sa 设强密码(至少 8 位、含大小写+数字+符号),并把当前 Windows 用户加为管理员(Add Current User)——PB 程序要跨机器连库,必须开 SQL 账号认证。
- 系统要求(官方):仅 64 位系统;Windows 10 1607+ / Windows 11 / Windows Server 2016+;内存最低 1GB(建议 4GB+);磁盘 6GB+;安装器会自动装 .NET Framework 4.7.2+ 与 VC++ 2015-2022 运行库,不用手动管。
装完打开 SSMS,服务器类型选 Database Engine,服务器名填 localhost\SQLEXPRESS(或 .\SQLEXPRESS),用 sa 登录验证一遍。
1.2 安装客户端驱动(关键,别再踩 SQLNCLI 的坑)
SQL Server 2022 官方支持的客户端连接方式只有两条线:
- ODBC Driver 18 for SQL Server(微软主推,独立安装包、持续更新)——推荐;
- OLE DB Driver 19 for SQL Server(新代 OLE DB Provider,MSOLEDBSQL19)。
而老的 SQL Native Client 11.0(SQLNCLI11)已停止维护、官方不支持连接 SQL Server 2022。所以无论你用哪种方式连,先把 ODBC Driver 18 for SQL Server 装上(上表有链接,装 64 位即可,PBIDEA 侧会自动找到 32 位副本一并装好)。
装好后验证驱动是否就位:开始菜单搜索「ODBC 数据源(32 位)」——注意必须是 32 位,因为 PB 是 32 位程序,配系统 DSN 时只能从 C:\Windows\SysWOW64\odbcad32.exe 打开,用 64 位管理器(System32)建了 DSN,PB 里永远找不到。在「系统 DSN」页点添加,应能看到「ODBC Driver 18 for SQL Server」。
小知识:为什么强调 32 位?PB/PBIDEA 的 exe 是 32 位进程,ODBC 管理器分 32/64 两套,DSN 注册表也是分区的。32 位程序只能读 32 位 DSN。这是「DSN 明明建了却连不上」的第一大原因。
1.3 打开 TCP/IP 与防火墙(远程连接时才需要)
本机 localhost\SQLEXPRESS 连不上通常是没启用 TCP/IP:打开「SQL Server 配置管理器」→ SQL Server 网络配置 → 对应实例的协议 → 把 TCP/IP 启用(Enabled),重启 SQL Server 服务;默认端口 1433(命名实例会动态分配端口,可在 TCP/IP 属性里固定为 1433)。远程机器连过来,还要在 Windows 防火墙放行 1433 端口。
二、建库 SQL(SSMS 里一次执行)
在 SSMS 中连上服务器后,新建查询,把下面整段跑一遍:建库 PbDemo、建表 t_user、插入 3 条示例数据、建 2 个存储过程(一个返回结果集、一个带输出参数)。
- -- ============ 1. 建库 ============
- IF DB_ID('PbDemo') IS NULL
- CREATE DATABASE PbDemo;
- GO
- USE PbDemo;
- GO
- -- ============ 2. 建表 t_user ============
- IF OBJECT_ID('t_user', 'U') IS NULL
- BEGIN
- CREATE TABLE t_user (
- id INT IDENTITY(1,1) PRIMARY KEY, -- 主键自增
- user_name NVARCHAR(50) NOT NULL, -- 姓名
- age INT NULL, -- 年龄
- salary DECIMAL(18,2) NULL, -- 月薪
- create_at DATETIME NOT NULL DEFAULT GETDATE() -- 创建时间
- );
- END
- GO
- -- ============ 3. 示例数据 ============
- IF NOT EXISTS (SELECT 1 FROM t_user)
- BEGIN
- INSERT INTO t_user (user_name, age, salary) VALUES (N'张三', 28, 8500.00);
- INSERT INTO t_user (user_name, age, salary) VALUES (N'李四', 34, 12000.50);
- INSERT INTO t_user (user_name, age, salary) VALUES (N'王五', 25, 6800.00);
- END
- GO
- -- ============ 4. 存过1:按年龄区间查用户(返回结果集) ============
- IF OBJECT_ID('usp_get_users_by_age', 'P') IS NOT NULL
- DROP PROCEDURE usp_get_users_by_age;
- GO
- CREATE PROCEDURE usp_get_users_by_age
- @min_age INT,
- @max_age INT
- AS
- BEGIN
- SET NOCOUNT ON;
- SELECT id, user_name, age, salary, create_at
- FROM t_user
- WHERE age BETWEEN @min_age AND @max_age
- ORDER BY id;
- END
- GO
- -- ============ 5. 存过2:按 id 查用户,姓名与年龄走输出参数 ============
- IF OBJECT_ID('usp_get_user_info', 'P') IS NOT NULL
- DROP PROCEDURE usp_get_user_info;
- GO
- CREATE PROCEDURE usp_get_user_info
- @uid INT,
- @u_name NVARCHAR(50) OUTPUT,
- @u_age INT OUTPUT
- AS
- BEGIN
- SET NOCOUNT ON;
- SELECT @u_name = user_name,
- @u_age = age
- FROM t_user
- WHERE id = @uid;
- END
- GO
复制代码
三、常见连接方式(三种,任选其一)
uo_database 的 DBMS 属性支持(见源码注释):'ODBC'、'MSS'(或 'Microsoft SQL Server'、'MSS Microsoft SQL Server')、'Oracle'、'MySQL'、'SQLite' 等。连 SQL Server 2022 最常用下面两种 + 一种配置文件方式。
3.1 方式一:DBMS='MSS' 直连(最简,推荐)
前置:已装 ODBC Driver 18 for SQL Server;SQL Server 服务已启动;PbDemo 库已按第二节建好。
步骤:1) 新建窗口,放一个按钮 cb_connect;2) 在 clicked 事件贴入下方代码;3) 把连接串里的服务器/账号/密码改成你自己的;4) 运行,弹出 SELECT 1 = 1 即连接成功。 - // 示例输入:服务器(本机 Express 命名实例;远程写 IP\实例名)
- string ls_server
- ls_server = 'localhost\SQLEXPRESS'
- // 示例输入:SQL 账号
- string ls_user
- ls_user = 'sa'
- // 示例输入:密码
- string ls_pass
- ls_pass = 'Pass@123'
- // 示例输入:数据库名
- string ls_db
- ls_db = 'PbDemo'
- // 组装连接串:ServerName + LogId + LogPass + Database 是 MSS 接口的标准四项
- string ls_dbparm
- ls_dbparm = 'ServerName=' + ls_server + ';LogId=' + ls_user + ';LogPass=' + ls_pass + ';Database=' + ls_db
- uo_database db
- db = create uo_database
- db.DBMS = 'MSS'
- db.DBParm = ls_dbparm
- if not db.Open() then
- MessageBox('连接失败', 'Open 返回 false,请检查驱动/服务/连接串')
- return
- end if
- // 连通性验证:SELECT 1 能取到值即说明已连上
- any la_one
- la_one = db.ExecuteScaler('SELECT 1')
- db.Close()
- destroy db
- MessageBox('连接成功', 'SELECT 1 = ' + string(la_one))
复制代码
3.2 方式二:DBMS='ODBC' 走系统 DSN(驱动可切换,最稳)
当直连在个别驱动版本下不稳定,或企业要求统一走 DSN 管理连接时,用这个方式。
建 DSN 步骤(必读):
- 打开 32 位 ODBC 管理器:C:\Windows\SysWOW64\odbcad32.exe(不要用 System32 那个 64 位的)。
- 「系统 DSN」→ 添加 → 选择 ODBC Driver 18 for SQL Server。
- 名称填 pb_sql2022,服务器填 localhost\SQLEXPRESS,身份验证选「SQL Server 认证」,账号 sa + 密码。
- 「下一步」勾选默认数据库 PbDemo,一路下一步,最后「测试数据源」显示成功即 DSN 可用。
前置:上一步的 32 位系统 DSN pb_sql2022 已建好并通过测试。
步骤:把下方代码贴到按钮 clicked,改 ConnectString 里的 UID/PWD 为你自己的账号。 - // 示例输入:DSN 名(32 位系统 DSN,用 SysWOW64\odbcad32.exe 建)
- string ls_dsn
- ls_dsn = 'pb_sql2022'
- // 示例输入:SQL 账号
- string ls_user
- ls_user = 'sa'
- // 示例输入:密码
- string ls_pass
- ls_pass = 'Pass@123'
- // ConnectString 是 ODBC 接口的标准连接串写法
- string ls_dbparm
- ls_dbparm = "ConnectString='DSN=" + ls_dsn + ";UID=" + ls_user + ";PWD=" + ls_pass + "'"
- uo_database db
- db = create uo_database
- db.DBMS = 'ODBC'
- db.DBParm = ls_dbparm
- if not db.Open() then
- MessageBox('连接失败', 'ODBC DSN 连接失败,请先在 odbcad32 里测试 DSN')
- return
- end if
- // 查行数验证
- uo_recordset rs
- rs = db.ExecuteSQL('SELECT COUNT(*) AS c FROM t_user')
- long ll_count
- ll_count = 0
- if rs.FetchNext() then
- ll_count = long(rs.GetNumber('c'))
- end if
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('ODBC 连接测试', 't_user 共 ' + string(ll_count) + ' 行')
复制代码
3.3 方式三:配置文件连接(一次保存、随处复用)
uo_database 支持把当前连接属性存成配置文件,以后直接 Open(配置文件) 一键连接,不用再拼连接串——适合把连接信息从代码里剥离出来统一管理。
前置:已能用方式一或方式二成功连上库。
步骤:1) 第一次运行时先执行 SaveConfig('D:\mssql2022.cfg') 保存;2) 之后任何地方 create uo_database 后直接 Open('D:\mssql2022.cfg') 即可。 - // 示例输入:配置文件路径(第一次连接成功后保存,之后复用)
- string ls_cfg
- ls_cfg = 'D:\mssql2022.cfg'
- uo_database db
- db = create uo_database
- // 第一次:连上后把属性存成配置文件(只跑一次,成功后注释掉下面两行)
- // db.DBMS = 'MSS'
- // db.DBParm = 'ServerName=localhost\SQLEXPRESS;LogId=sa;LogPass=Pass@123;Database=PbDemo'
- // 之后每次都直接 Open(配置文件),无需再设属性
- if not db.Open(ls_cfg) then
- MessageBox('连接失败', '打开配置文件连接失败:' + ls_cfg)
- return
- end if
- // 用 ExecuteScaler 验证
- any la_one
- la_one = db.ExecuteScaler('SELECT 1')
- db.Close()
- destroy db
- MessageBox('配置文件连接成功', 'SELECT 1 = ' + string(la_one))
复制代码
四、增删改查(完整代码)
4.1 查询并遍历结果集(无参数)
前置:PbDemo 库与 t_user 表已建好;以下代码段均需先按 3.1 建立 db 连接(或直接拼进一个按钮里)。
步骤:贴入代码 → 运行 → MessageBox 输出遍历到的姓名列表。 - // 示例输入:连接信息(同 3.1,这里省略,假设 db 已连接)
- // 1) 执行查询,返回结果集对象
- uo_recordset rs
- rs = db.ExecuteSQL('SELECT id, user_name, age, salary FROM t_user ORDER BY id')
- // 2) 逐行遍历:FetchNext 取下一行,取完返回 false
- string ls_names
- ls_names = ''
- long ll_rows
- ll_rows = 0
- do while rs.FetchNext()
- ll_rows++
- // GetString 按列名取值;GetNumber 取数值列(返回 double,用 long() 包一层)
- if ll_rows > 1 then
- ls_names = ls_names + '、'
- end if
- ls_names = ls_names + string(rs.GetNumber('id')) + ':' + rs.GetString('user_name')
- loop
- // 3) 收尾:先关结果集再关连接
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('查询结果', '共 ' + string(ll_rows) + ' 行:' + ls_names)
复制代码
4.2 参数化查询(PrepareSQL + BindParams,防注入)
直接拼字符串拼 SQL 有注入风险,且字符串里带单引号容易出错。用 PrepareSQL 预编译 + BindParams 按位置绑参数:
前置:同 4.1,db 已连接;t_user 已建。
步骤:贴入下方代码,运行后 MessageBox 显示「年龄 ≥ 30 的用户数」。 - // 示例输入:查询条件(最小年龄)
- long ll_min_age
- ll_min_age = 30
- // 1) 预编译:SQL 里用 ? 占位(PrepareSQL 返回 uo_recordset)
- uo_recordset rs
- rs = db.PrepareSQL('SELECT COUNT(*) AS c FROM t_user WHERE age >= ?')
- // 2) 按位置绑定参数(第 1 个 ? 绑 ll_min_age)
- if not rs.BindParams(ll_min_age) then
- MessageBox('绑定失败', 'BindParams 返回 false')
- return
- end if
- // 3) 执行
- if not rs.ExecuteSQL() then
- MessageBox('执行失败', 'ExecuteSQL 返回 false')
- return
- end if
- // 4) 取结果
- long ll_cnt
- ll_cnt = 0
- if rs.FetchNext() then
- ll_cnt = long(rs.GetNumber('c'))
- end if
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('参数化查询', '年龄 >= ' + string(ll_min_age) + ' 的用户共 ' + string(ll_cnt) + ' 人')
复制代码
4.3 新增 / 修改 / 删除(ExecuteSQL + RowsAffected)
DML 语句同样用 ExecuteSQL 执行(没有结果集,执行后查 RowsAffected() 看影响行数):
前置:db 已连接;t_user 已建。
步骤:1) 运行下方代码会在 t_user 插入一条新记录、把 id=1 的工资改掉、删掉 id=3;2) MessageBox 分别显示三步的影响行数。 - // 示例输入:新增记录的值
- string ls_new_name
- ls_new_name = '赵六'
- int li_new_age
- li_new_age = 41
- // 示例输入:修改的 id 与新工资
- long ll_upd_id
- ll_upd_id = 1
- decimal ldec_new_salary
- ldec_new_salary = 9999.50
- // 示例输入:要删除的 id
- long ll_del_id
- ll_del_id = 3
- // 1) 新增(SQL 里中文建议加 N 前缀,避免乱码)
- // 注意:ExecuteSQL 返回 uo_recordset 对象(DML 无结果集),
- // 影响行数必须用 db.RowsAffected() 取,不能把返回对象当 long 用
- uo_recordset rs_ins
- rs_ins = db.ExecuteSQL("INSERT INTO t_user(user_name, age, salary) VALUES(N'" + ls_new_name + "', " + string(li_new_age) + ", 7500.00)")
- long ll_ins
- ll_ins = db.RowsAffected()
- rs_ins.Close()
- destroy rs_ins
- // 2) 修改
- uo_recordset rs_upd
- rs_upd = db.ExecuteSQL("UPDATE t_user SET salary = " + string(ldec_new_salary) + " WHERE id = " + string(ll_upd_id))
- long ll_upd
- ll_upd = db.RowsAffected()
- rs_upd.Close()
- destroy rs_upd
- // 3) 删除
- uo_recordset rs_del
- rs_del = db.ExecuteSQL('DELETE FROM t_user WHERE id = ' + string(ll_del_id))
- long ll_del
- ll_del = db.RowsAffected()
- rs_del.Close()
- destroy rs_del
- db.CommitTrans()
- MessageBox('DML 结果', '新增影响 ' + string(ll_ins) + ' 行,修改 ' + string(ll_upd) + ' 行,删除 ' + string(ll_del) + ' 行')
复制代码提醒:上面为了演示用了字符串拼接;生产代码请改用 4.2 的参数化写法,把值交给 BindParams 处理,杜绝注入和引号转义问题。多语句要么全部成功要么全部失败时,见 4.4 事务。
4.4 事务:多语句要么全成、要么全滚
前置:db 已连接。
步骤:模拟「转账」——张三扣 1000、李四加 1000,两条 UPDATE 都成功才提交,否则回滚。可故意把第二条 SQL 写错来验证回滚。 - // 示例输入:转账金额
- decimal ldec_amount
- ldec_amount = 1000.00
- // 1) 张三扣款(ExecuteSQL 返回 uo_recordset,影响行数用 db.RowsAffected() 取)
- uo_recordset rs1
- rs1 = db.ExecuteSQL("UPDATE t_user SET salary = salary - " + string(ldec_amount) + " WHERE user_name = N'张三'")
- long ll_r1
- ll_r1 = db.RowsAffected()
- rs1.Close()
- destroy rs1
- // 2) 李四入账(把表名故意写错可验证回滚:t_user_xxx)
- uo_recordset rs2
- rs2 = db.ExecuteSQL("UPDATE t_user SET salary = salary + " + string(ldec_amount) + " WHERE user_name = N'李四'")
- long ll_r2
- ll_r2 = db.RowsAffected()
- rs2.Close()
- destroy rs2
- // 3) 两条都成功才提交,否则回滚
- string ls_msg
- ls_msg = ''
- if ll_r1 > 0 and ll_r2 > 0 then
- db.CommitTrans()
- ls_msg = '转账成功,已提交'
- else
- db.RollbackTrans()
- ls_msg = '转账失败,已回滚(第1步影响 ' + string(ll_r1) + ' 行,第2步影响 ' + string(ll_r2) + ' 行)'
- end if
- MessageBox('事务演示', ls_msg)
复制代码
五、存储过程(三种典型场景)
5.1 调用返回结果集的存储过程
usp_get_users_by_age(@min_age, @max_age) 返回一个结果集,用 PrepareSQL + BindParams + FetchNext 遍历:
前置:第二节的存过已创建;db 已连接。
步骤:运行下方代码,MessageBox 显示年龄 25–35 的用户姓名列表。 - // 示例输入:年龄区间
- int li_min
- li_min = 25
- int li_max
- li_max = 35
- // 1) 预编译存储过程调用:{call 过程名(?, ?)} 是标准写法
- uo_recordset rs
- rs = db.PrepareSQL('{call usp_get_users_by_age(?, ?)}')
- // 2) 按位置绑定两个入参
- if not rs.BindParams(li_min, li_max) then
- MessageBox('绑定失败', 'BindParams 返回 false')
- return
- end if
- // 3) 执行
- if not rs.ExecuteSQL() then
- MessageBox('执行失败', 'ExecuteSQL 返回 false')
- return
- end if
- // 4) 遍历结果集
- string ls_names
- ls_names = ''
- long ll_rows
- ll_rows = 0
- do while rs.FetchNext()
- ll_rows++
- if ll_rows > 1 then
- ls_names = ls_names + '、'
- end if
- ls_names = ls_names + rs.GetString('user_name') + '(' + string(long(rs.GetNumber('age'))) + '岁)'
- loop
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('存储过程结果集', '年龄 ' + string(li_min) + '-' + string(li_max) + ' 共 ' + string(ll_rows) + ' 人:' + ls_names)
复制代码
5.2 带输出参数的存储过程
usp_get_user_info(@uid, @u_name OUTPUT, @u_age OUTPUT) 不返回结果集,值从 OUTPUT 参数带回。必须用 BindParam(单个绑定)并传 ref 变量 + 长度,不能只用 BindParams:
前置:存过已创建;db 已连接。
步骤:运行下方代码,MessageBox 显示 id=1 的用户姓名与年龄(出参值已写回变量)。 - // 示例输入:要查询的用户 id(入参)
- long ll_uid
- ll_uid = 1
- // 1) 预编译
- uo_recordset rs
- rs = db.PrepareSQL('{call usp_get_user_info(?, ?, ?)}')
- // 2) 第 1 个 ? 绑入参
- if not rs.BindParam(1, ll_uid) then
- MessageBox('绑定失败', '入参绑定失败')
- return
- end if
- // 3) 第 2、3 个 ? 绑出参:必须 ref 变量 + 长度(字符串长度=字节上限,数值给 8 即可)
- string ls_name
- ls_name = ''
- long ll_age
- ll_age = 0
- if not rs.BindParam(2, ls_name, 50) then
- MessageBox('绑定失败', '出参1绑定失败')
- return
- end if
- if not rs.BindParam(3, ll_age, 8) then
- MessageBox('绑定失败', '出参2绑定失败')
- return
- end if
- // 4) 执行:成功后出参值已写回 ls_name / ll_age
- if not rs.ExecuteSQL() then
- MessageBox('执行失败', 'ExecuteSQL 返回 false')
- return
- end if
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('存储过程出参', 'id=' + string(ll_uid) + ' 的用户是 ' + ls_name + ',' + string(ll_age) + ' 岁')
复制代码
5.3 存储过程返回多个结果集(HasResultSet 循环)
个别业务存过会 SELECT 多次(如先查汇总、再查明细)。uo_recordset 的 HasResultSet() 专门处理这种情况:第一个结果集 FetchNext 取完返回 false 后,判断 HasResultSet() 为 true 就继续取下一个结果集:
- // 示例输入:部门 id(假设存过 usp_emp_and_dept 先返回部门信息、再返回员工列表)
- long ll_dept
- ll_dept = 1
- uo_recordset rs
- rs = db.PrepareSQL('{call usp_emp_and_dept(?)}')
- if not rs.BindParams(ll_dept) then
- MessageBox('绑定失败', 'BindParams 返回 false')
- return
- end if
- if not rs.ExecuteSQL() then
- MessageBox('执行失败', 'ExecuteSQL 返回 false')
- return
- end if
- // 第 1 个结果集
- string ls_dept_name
- ls_dept_name = ''
- if rs.FetchNext() then
- ls_dept_name = rs.GetString('dept_name')
- end if
- // 第 2 个结果集:先判断 HasResultSet,再继续 FetchNext
- string ls_emps
- ls_emps = ''
- long ll_cnt
- ll_cnt = 0
- if rs.HasResultSet() then
- do while rs.FetchNext()
- ll_cnt++
- if ll_cnt > 1 then
- ls_emps = ls_emps + '、'
- end if
- ls_emps = ls_emps + rs.GetString('emp_name')
- loop
- end if
- db.CommitTrans()
- rs.Close()
- destroy rs
- db.Close()
- destroy db
- MessageBox('多结果集', '部门:' + ls_dept_name + ';员工 ' + string(ll_cnt) + ' 人:' + ls_emps)
复制代码说明:usp_emp_and_dept 未在第二节脚本中定义,仅用于演示多结果集遍历模式;实际使用时把它替换成你自己的、确实会返回多个结果集的存过即可。
六、常见坑与排查(按命中率排序)
| 现象 | 根因 | 解法 | | 08001 SQL Server Native Client 11.0 不支持连接到 SQL Server 2022 | 客户端在用已淘汰的 SQLNCLI11 | 装 ODBC Driver 18(1.2 节),连接改走 3.2 的 DSN 或确认驱动版本 | | DSN 明明建了,PB 里就是找不到 | 用 64 位 ODBC 管理器建的 DSN | 必须用 C:\Windows\SysWOW64\odbcad32.exe 建32 位系统 DSN | | localhost\SQLEXPRESS 连不上 | 命名实例 TCP/IP 未启用 | SQL Server 配置管理器 → 实例协议 → 启用 TCP/IP,重启服务 | | 远程连不上 | 防火墙挡了 1433 | 防火墙放行 1433 端口;命名实例建议固定端口 | | sa 登录失败(18456) | 服务器认证模式是 Windows-only | 安装时选混合模式,或 SSMS 里服务器属性→安全性→SQL Server 和 Windows 认证模式,重启服务 | | SQL 里中文查出来是乱码 | 字符串没带 N 前缀 | 常量用 N'中文';参数化查询传 BindParams 时无此问题 | | BindParam 出参没值 | 出参没写 ref 或没给长度 | 必须 BindParam(位置, ref 变量, 长度),字符串长度给 50/100 等字节上限 | | 存储过程执行报参数数量不匹配 | ? 数量与绑定数不一致 | 数清楚 SQL 里的 ?,入参出参按顺序一一对应 |
小结
- 环境三件套:SQL Server 2022(Express 免费)+ SSMS + ODBC Driver 18 for SQL Server(客户端必装);DSN 一律走 32 位管理器。
- 三种连接:DBMS='MSS' 直连最简;DBMS='ODBC' + 32 位系统 DSN 最稳;SaveConfig + Open(配置文件) 适合连接信息集中管理。
- 增删改查:ExecuteSQL 返回 uo_recordset,FetchNext+GetString/GetNumber 取值;DML 用 db.RowsAffected() 看影响行数;参数化用 PrepareSQL+BindParams(SQL 里 ? 占位);事务 CommitTrans/RollbackTrans。
- 存储过程:结果集用 {call 存过名(?, ?)} + FetchNext 遍历;输出参数必须 BindParam(位置, ref 变量, 长度);多结果集用 HasResultSet() 衔接。
- 资源释放顺序固定:先 rs.Close()+destroy rs,再 db.Close()+destroy db。
—— pbai |