VB.NET调用SQL Server参数化INSERT报110错误:列数与VALUES值数不匹配
问题描述
我的VB.NET应用的一位瑞典用户反馈执行查询时出现错误#110,错误信息为There are fewer columns in the INSERT statement than values specified in the VALUES clause.,他发来的报错截图如下:
报错信息的结构如下:
$"DoSql Error #{ex.Number}: {ex.Message}; sql: {sql}; Item={iType}:{iNo}:{iColor}:{iCond}"
我在VB.NET中使用参数化查询调用SQL Server插入语句,指定插入18列,未使用字符串插值,相关代码如下:
Private Sub UpdatePrices(iNo As String, iType As String, iColor As Integer, iCond As String) ... 'Retrieve pgonline Json result from web server ' Dim sqlParams = GetParams({ {"@Item", iNo}, {"@Color", iColor}, {"@Cond", iCond}, {"@Type", iType}}, {"@Curr", pgOnline("currency_code").ToString}, {"@SoldMinPrice", pgOnline("min_price").Value(Of Single)}, {"@SoldMaxPrice", pgOnline("max_price").Value(Of Single)}, {"@SoldAvgPrice", pgOnline("avg_price").Value(Of Single)}, {"@SoldQtyAvgPrice", pgOnline("qty_avg_price").Value(Of Single)}, {"@SoldLotQty", Math.Min(32767, pgOnline("unit_quantity").Value(Of Integer))}, {"@SoldItemQty", Math.Min(32767, pgOnline("total_quantity").Value(Of Integer))}, {"@StockMinPrice", pgOnline("min_price").Value(Of Single)}, {"@StockMaxPrice", pgOnline("max_price").Value(Of Single)}, {"@StockAvgPrice", pgOnline("avg_price").Value(Of Single)}, {"@StockQtyAvgPrice", pgOnline("qty_avg_price").Value(Of Single)}, {"@StockLotQty", pgOnline("unit_quantity").Value(Of Integer)}, {"@StockItemQty", Math.Min(32767, pgOnline("total_quantity").Value(Of Integer))} })) Dim sql = "INSERT INTO [Bricklink].[Price_Guide] (PriceDate,ItemNo,ItemType,Color,Cond,Curr,SoldMinPrice,SoldMaxPrice,SoldAvgPrice,SoldQtyAvgPrice, SoldLotQty, SoldItemQty, StockMinPrice, StockMaxPrice, StockAvgPrice, StockQtyAvgPrice, StockLotQty, StockItemQty) VALUES(CURRENT_TIMESTAMP, @Item, @Type, @Color, @Cond, @Curr, @SoldMinPrice, @SoldMaxPrice, @SoldAvgPrice, @SoldQtyAvgPrice, @SoldLotQty, @SoldItemQty, @StockMinPrice, @StockMaxPrice, @StockAvgPrice, @StockQtyAvgPrice, @StockLotQty, @StockItemQty);" DoSql(sql, sqlParams, True)
INSERT语句和VALUES子句均包含18个列,参数混合为.NET的Single或Integer数值类型,数据从服务端拉取:我在美国本地拉取时,Single类型的价格无千位分隔符,保留3位小数。
需要注意的是我本地从未遇到该错误,该价格更新查询每日在本地运行数千次,瑞典用户是少数非美国用户之一。我猜测是瑞典地区的数据结构差异或设备数据解析逻辑差异,导致SQL Server误读数值:原本猜想数值中的逗号会被识别为字段分隔符,让SQL认为VALUES中的列数多于INSERT指定列数。
但我在本地测试将小数点替换为逗号时,仅会触发算术溢出错误,不会将数值中的逗号识别为查询语句的分隔符;且无销售数据时服务端所有字段返回0,不存在空值或空白值。
想确认三个问题:
- 参数化查询的参数是否可能触发此类行为?
- 若可能,我该如何修改查询解决该问题?
- 若不可能,还有哪些差异可能导致SQL Server认为该INSERT、VALUES均为18列的查询存在列数不匹配问题?
附录
查询调用方法DoSql的实现代码如下:
Public Function DoSql(sql As String, Optional ByVal params As List(Of SqlParameter) = Nothing, Optional throwException As Boolean = False) As Boolean Using conn As New SqlConnection(GetDBConnectionString) Return DoSql(conn, sql, params, throwException) End Using End Function Public Function DoSql(conn As SqlConnection, sql As String, Optional ByVal params As List(Of SqlParameter) = Nothing, Optional throwException As Boolean = False) As Boolean Dim cmd As New SqlCommand(sql, conn) If Not IsNothing(params) Then For Each param In params cmd.Parameters.Add(param) Next End If If cmd.Connection.State <> ConnectionState.Open Then cmd.Connection.Open() Try Dim rows As Integer = cmd.ExecuteNonQuery() cmd.Parameters.Clear() Return (rows > 0) Catch ex As SqlException Dim paramStg As String = "" If params IsNot Nothing Then For Each param In params paramStg += param.ParameterName & "=" & param.Value.ToString & "; " Next End If Log($"DoSql Error {ex.Number}: {ex.Message} executing: {sql} with params={paramStg}", 2) cmd.Parameters.Clear() If throwException Then Throw ex MsgBoxASync($"DoSql Error #{ex.Number}: {ex.Message}; parameters: {paramStg}; sql: {sql}") Return False Catch ex As Exception Dim paramStg As String = "" If params IsNot Nothing Then For Each param In params paramStg += param.ParameterName & "=" & param.Value.ToString & "; " Next End If Log($"Error {ex.Message} occured executing the DoSQL command {sql} with params={paramStg}", 2) cmd.Parameters.Clear() Return False End Try End Function
解答
- 参数化查询本身不可能触发此类行为。参数化查询的参数值是独立于SQL语句文本传输的,不会和SQL本体拼接,无论参数值包含逗号、引号还是其他特殊字符,都不会被SQL Server解析为SQL语法的一部分,你本地的测试结果也验证了这一点。
- 该场景不需要针对参数化查询本身做修改,可按下面的排查方案定位问题。
- 可能导致该错误的差异点如下:
- 表结构不一致:确认瑞典用户连接的数据库实例中
[Bricklink].[Price_Guide]表结构和你本地完全一致,包括列的数量、顺序,部分用户如果使用本地自建库,可能存在表结构未同步更新的情况。 GetParams方法逻辑缺陷:如果该方法内部没有显式给SqlParameter指定SqlDbType,在瑞典区域的文化设置下,数值类型的参数可能被自动推断为字符串类型,若同时存在JSON解析错误导致返回的字段数量异常,可能生成错误的参数列表。- SQL语句存在隐藏特殊字符:你编写的INSERT语句中如果包含全角空格、不可见控制字符,在瑞典区域的系统编码下可能被SQL Server错误识别为值分隔符,导致VALUES子句被拆分为更多项。
- JSON解析异常:瑞典用户拉取的服务端返回结果如果存在格式异常,
pgOnline读取字段时触发静默错误,导致部分参数的实际值为异常类型,间接触发SQL执行逻辑异常。
- 表结构不一致:确认瑞典用户连接的数据库实例中
修复建议
- 先让报错用户导出弹窗中的完整SQL和参数内容,手动统计INSERT后的列数、VALUES后的项数,确认是否真的存在数量不一致的情况。
- 修改参数创建逻辑,给每个参数显式指定对应的
SqlDbType,例如Single类型指定为SqlDbType.Real,Integer类型指定为SqlDbType.Int,避免自动类型推断异常。 - 在调用
DoSql方法前增加临时日志,输出完整的SQL语句文本、参数数量、每个参数的类型和值,确认执行前的逻辑和预期一致。
内容的提问来源于stack exchange,提问作者Scott
相关产品推荐
相关产品推荐

