【问题标题】:SqlBulkCopy InvalidOperationException: The given value of type SqlDecimal from the data source cannot be converted to type decimal of the specifiedSqlBulkCopy InvalidOperationException:来自数据源的 SqlDecimal 类型的给定值无法转换为指定的十进制类型
【发布时间】:2021-01-01 13:36:36
【问题描述】:

我正在运行控制台应用程序并使用 SqlBulkCopy 将记录从 .NET DataTable 写入 MSSQL 数据库。

.NET 中有一列:new DataColumn(columnName, typeof(decimal));,其中一行的值为:384.50000。

运行 SqlBulkCopy 时(在具有较小值的批次上运行良好)我收到以下错误:

System.InvalidOperationException: The given value of type SqlDecimal from the data source cannot be converted to type decimal of the specified target column. ---> System.InvalidOperationException: The given value of type SqlDecimal from the data source cannot be converted to type decimal of the specified target column. ---> System.ArgumentException: Parameter value '384.50000' is out of range.
   --- End of inner exception stack trace ---
   at System.Data.SqlClient.SqlBulkCopy.ConvertValue(Object value, _SqlMetaData metadata, Boolean isNull, Boolean& isSqlType, Boolean& coercedToDataFeed)
   --- End of inner exception stack trace ---
   at System.Data.SqlClient.SqlBulkCopy.ConvertValue(Object value, _SqlMetaData metadata, Boolean isNull, Boolean& isSqlType, Boolean& coercedToDataFeed)
   at System.Data.SqlClient.SqlBulkCopy.ReadWriteColumnValueAsync(Int32 col)
   at System.Data.SqlClient.SqlBulkCopy.CopyColumnsAsync(Int32 col, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyRowsAsync(Int32 rowsSoFar, Int32 totalRows, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinued(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsync(BulkCopySimpleResultSet internalResults, String updateBulkCommandText, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalRestContinuedAsync(BulkCopySimpleResultSet internalResults, CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalRestAsync(CancellationToken cts, TaskCompletionSource`1 source)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServerInternalAsync(CancellationToken ctoken)
   at System.Data.SqlClient.SqlBulkCopy.WriteRowSourceToServerAsync(Int32 columnCount, CancellationToken ctoken)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServer(DataTable table, DataRowState rowState)
   at System.Data.SqlClient.SqlBulkCopy.WriteToServer(DataTable table)

目标数据库表中的列如下:

,[Test_Column] DECIMAL(38,5) NULL

根据此处的文档: https://docs.microsoft.com/en-us/sql/t-sql/data-types/decimal-and-numeric-transact-sql?view=sql-server-ver15

decimal[ (p[ ,s] )] p(精度)要存储的十进制数字的最大总数。这个数字包括小数点的左边和右边。精度必须是从 1 到最大精度 38 之间的一个值。

鉴于这种情况,我不明白为什么会出现错误:

The given value of type SqlDecimal from the data source cannot be converted to type decimal of the specified target column. ---> System.ArgumentException: Parameter value '384.50000' is out of range.

谁能解释一下如何解决这个问题?

我已尝试将 db 类型更改为 float 以查看是否可行,但是当我将来自 csv 的值转换为 .NET DataTable 时,我无法放弃指数表示法。当它是十进制时,这有效:

Decimal.Parse("-5E-05", NumberStyles.Float);    -0.00005    decimal

但是当它是浮动时,这不起作用:

(float)Decimal.Parse(text, NumberStyles.Float); -5E-05  float

另外值得一提的是,我有在 SqlBulkCopy 中添加列映射的代码。 CSV、.NET DataTable 和 DB 表都是从我在 .NET 中使用的相同类型定义创建的。例如;在 SQL 中它是 Decimal(38,5) 它是 .NET DataTable 中的小数,并且列名在 CSV、.NET Datatabe 和 DB 表中都匹配。

环境: MSSQL Server 2019 开发者版 视窗 10
C# 控制台应用程序 .NET 4.7.2 视觉工作室 2019

【问题讨论】:

  • 将DataTable列的类型设置为SqlDecimal,并用new SqlDecimal(val)加载每个值

标签: sql-server tsql sqlbulkcopy sql-server-2019


【解决方案1】:

原来还有另一列分配了错误的小数精度。 我打算删除这个问题,因为它不会真正帮助任何人,因为我有错,但 SO 建议不要这样做。

【讨论】:

    【解决方案2】:

    在 DataTable 中使用 SqlDecimal 是不常见的。通常您在 DataTable 中使用 .NET 基元,因此它通常是 Decimal,而不是 SqlDecimal。

    但我无法重现您描述的行为。 EG

    using System.Data.SqlClient;
    using System;
    using System.Data;
    using System.Data.SqlTypes;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace SqlClientTest
    {
    
        class Program
        {
    
            static void Main(string[] args)
            {
                var constr = @"server=localhost;database=tempdb;Integrated Security=true";
    
                using (var con = new SqlConnection(constr))
    
                {
                    con.Open();
                    var cmd = new SqlCommand("create table #t(id int, val decimal(38,5))", con);
                    cmd.ExecuteNonQuery();
    
                    var dt = new DataTable();
                    dt.Columns.Add("id", typeof(int));
                    dt.Columns.Add("val", typeof(SqlDecimal));
    
                    var r = dt.Rows.Add();
                    r["id"] = 1;
                    r["val"] = new SqlDecimal(384.50000);
    
                    using (var bc = new SqlBulkCopy(con))
                    {
                        bc.DestinationTableName = "#t";
    
                        bc.ColumnMappings.Add(new SqlBulkCopyColumnMapping("id", "id"));
                        bc.ColumnMappings.Add(new SqlBulkCopyColumnMapping("val", "val"));
    
                        bc.WriteToServer(dt);
    
                        cmd = new SqlCommand("select * from #t", con);
                        using (var rdr = cmd.ExecuteReader())
                        {
                            while (rdr.Read())
                            {
                                Console.WriteLine($"{r[0]} {r[1]}");
                            }
                        }
                    }
    
    
                }
            }
    
    
        }
    
    }
    

    【讨论】:

    • 我尝试使用 SqlDecimal 但它仍然失败并出现同样的错误。我将整理一个演示项目,现在可以复制该问题。我会用这个更新问题。
    • 原来还有另一列(我的数据集有超过 100 个列),它有 DECIMAL(7,5) 并且有超过这个的数据。
    猜你喜欢
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多