为什么我的数据库命令总是调用sp_describe_first_result_set?
背景
我正在维护一个用于将记录导出到平面文件的VB.Net(原VB6)工具。有客户反馈新版本运行耗时很长,深入跟踪后很快找到了原因(内容已做模糊处理):
exec [sys].sp_describe_first_result_set N'Update [MAIN].[dbo].[Inventory] Set ExportId = @P1 Where Comp = @P2 and Osite = @P3 and Key = @P4', N'@P1 numeric(10),@P2 varchar(6),@P3 varchar(6),@P4 int',1
这类语句每次执行要花费半秒。该工具需要更新库存主表的导出相关信息,而这个表有大量触发器,因此sp_describe_first_result_set必须模拟所有触发器执行才能确定结果,这点我在后续跟踪中已经验证。
问题
我无法理解为什么代码会针对update语句调用sp_describe_first_result_set,毕竟update语句根本没有返回结果集。命令的配置看起来也没有异常:
Dim connection = New ADODB.Connection connection.ConnectionString = config.AdoConnectionString connection.CursorLocation = CursorLocationEnum.adUseClient connection.Open() cmd = New ADODB.Command With cmd .ActiveConnection = connection .CommandType = CommandTypeEnum.adCmdText .CommandText = "Update [MAIN].[dbo].[Inventory] " & "Set ExportId = ? " & "Where Comp = ? and Osite = ? and Key = ?" .Parameters.Append(NumericParameter(cmd.CreateParameter("ExportId", DataTypeEnum.adNumeric, ParameterDirectionEnum.adParamInput), 10, 0)) .Parameters.Append(cmd.CreateParameter("Comp", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 6)) .Parameters.Append(cmd.CreateParameter("Osite", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 6)) .Parameters.Append(cmd.CreateParameter("Key", DataTypeEnum.adInteger, ParameterDirectionEnum.adParamInput)) End With cmd.Parameters("ExportID").Value = lExportId cmd.Parameters("Comp").Value = sComp cmd.Parameters("Osite").Value = sOsite cmd.Parameters("Key").Value = iKey cmd.Execute()
请问我是不是漏掉了什么配置才导致系统持续调用sp_describe_first_result_set?有没有办法可以禁止该行为?
解决方案
根本原因
这个问题的触发根源是你设置了CursorLocation = CursorLocationEnum.adUseClient(客户端游标)。
当使用客户端游标时,ADODB的SQL Server驱动会默认对所有执行的语句做结果集元数据预检测,哪怕是UPDATE这类本身没有返回结果集的语句,也会调用sp_describe_first_result_set模拟执行语句,提前获取可能返回的结果集结构。你的表有大量触发器,模拟执行的成本极高,就出现了单语句耗时半秒的问题。
修复方案
你可以根据自己的业务场景选择以下任意一种方案:
- 方案1:不需要使用客户端游标特性的前提下,直接把游标位置改为服务端游标
把代码里的connection.CursorLocation = CursorLocationEnum.adUseClient替换为connection.CursorLocation = CursorLocationEnum.adUseServer,服务端游标不会触发结果集预检测逻辑,直接执行UPDATE语句即可。 - 方案2:必须保留客户端游标时,在执行命令时添加
adExecuteNoRecords参数,明确告知驱动当前语句没有返回结果集
把最后一行的cmd.Execute()修改为:
这个参数会让驱动跳过结果集元数据检测的步骤,不会再调用cmd.Execute RecordsAffected:=Nothing, Options:=CommandTypeEnum.adCmdText Or ExecuteOptionEnum.adExecuteNoRecordssp_describe_first_result_set。 - 方案3:把UPDATE逻辑封装为无返回结果集的存储过程,调用时把
CommandType设置为adCmdStoredProc,也可以规避预检测逻辑,性能优于直接执行动态SQL。
修改完成后可以通过SQL Server Profiler再次跟踪确认,sp_describe_first_result_set的调用会直接消失,更新操作的耗时会恢复到正常水平。
内容的提问来源于stack exchange,提问作者Glazius
相关产品推荐
相关产品推荐

