QSqlQuery无法获取SQL Server存储过程输出值问题排查
问题描述
将VB.NET编写的Web服务器迁移至Qt 5.15.12,需访问SQL Server 2012数据库。其中一个用于新增/更新记录的存储过程,在VB.NET中输出参数正常,但Qt调用时出现异常:传入不存在的键时能正确获取输出值,传入已存在的键时输出参数始终是初始值-1。
存储过程代码:
CREATE PROCEDURE AddUpdateRecord @Param1 NChar(12), @Param2 NVarChar(80), @RetVal Int Output AS BEGIN SET NOCOUNT ON SET @RetVal = 0 SELECT Column1 FROM MyTable WHERE Column1=@Param1 IF @@ROWCOUNT > 0 BEGIN UPDATE MyTable SET Column2=@Param2 WHERE Column1=@Param1 SET @RetVal = @@ROWCOUNT END ELSE BEGIN INSERT INTO MyTable (Column1, Column2) VALUES (@Param1, @Param2) SET @RetVal = @@ROWCOUNT END END
Qt调用代码:
QSqlQuery qry(dbObject); qry.prepare("execute AddUpdateRecord ?, ?, ?"); qry.bindValue(0, "012345678912"); qry.bindValue(1, "ABCDEFGHIJKLMNOP"); qry.bindValue(2, QVariant(int(-1)), QSql::Out); if ( qry.exec() && (qry.boundValue(2).toInt() > 0) ) { return(true); } return(false);
问题原因与修复方案
核心问题:未消费存储过程产生的结果集
当传入已存在的键时,存储过程中的SELECT Column1 FROM MyTable WHERE Column1=@Param1会返回一个非空结果集。Qt的QSqlQuery在处理存储过程时,必须先消费完所有生成的结果集,才能正确读取输出参数的值。而传入不存在的键时,SELECT返回空结果集,Qt内部自动处理了这个空集,所以能正常获取输出参数。
方案1:修改存储过程,移除多余的SELECT语句
原存储过程用SELECT判断记录存在性完全可以用EXISTS替代,避免生成额外结果集:
CREATE PROCEDURE AddUpdateRecord @Param1 NChar(12), @Param2 NVarChar(80), @RetVal Int Output AS BEGIN SET NOCOUNT ON SET @RetVal = 0 IF EXISTS(SELECT 1 FROM MyTable WHERE Column1=@Param1) BEGIN UPDATE MyTable SET Column2=@Param2 WHERE Column1=@Param1 SET @RetVal = @@ROWCOUNT END ELSE BEGIN INSERT INTO MyTable (Column1, Column2) VALUES (@Param1, @Param2) SET @RetVal = @@ROWCOUNT END END
方案2:在Qt代码中手动消费结果集
如果不想修改存储过程,在执行exec()后,需要调用next()遍历结果集,或者直接调用finish()来结束结果集的处理,之后再读取输出参数:
QSqlQuery qry(dbObject); qry.prepare("execute AddUpdateRecord ?, ?, ?"); qry.bindValue(0, "012345678912"); qry.bindValue(1, "ABCDEFGHIJKLMNOP"); qry.bindValue(2, QVariant(int(-1)), QSql::Out); if (qry.exec()) { // 消费SELECT生成的结果集 while (qry.next()) {} // 或者直接调用finish() // qry.finish(); if (qry.boundValue(2).toInt() > 0) { return true; } } return false;
额外注意事项
- 建议显式指定输出参数的类型,避免驱动类型匹配问题:
qry.bindValue(2, QVariant::fromValue<int>(-1), QSql::Out); - 存储过程中
SET NOCOUNT ON的设置是正确的,能避免返回额外的影响行数结果集,减少干扰。
内容的提问来源于stack exchange,提问作者Jim Rhodes
相关产品推荐
相关产品推荐

