我正在尝试使用BulkCopy.WriteToServer()从Oracle到SQL Server批量插入1000万行。
我确定了
当涉及大约140万行时,它因System.ArgumentOutOfRangeException而失败:Hour,Minute和Second参数描述了一个不可表示的DateTime。在System.DateTime.DateToTicks(Int32年,Int32月,Int32日)
这是我的代码
SqlBulkCopy copy;
copy = new SqlBulkCopy(destConn, SqlBulkCopyOptions.TableLock, null);
// ColumnMappings property is used to map column positions, not data type
copy.DestinationTableName = DestTable;
copy.NotifyAfter = 5000;
copy.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
copy.BulkCopyTimeout = 0;
try { copy.WriteToServer((IDataReader)rd); }
catch (Exception ex)
{
AppInfo.TableMsg[SrcTable] = AppInfo.TableMsg[SrcTable] + "\r\n" + "bulkcopy.WriteToServer(rd) failed. " + ex.Message;
throw ex;
}
我的表格超过100列,有26个DATE列。很难理清坏数据的位置
所以我在这里有3个问题
谢谢,
更新: 我做了以下
将选择列表修改为
当my_date_column< To_Date('01 / 01/1753','mm / dd / yyyy')那么To_Date('01 / 01/1753','mm / dd / yyyy')ELSE my_date_column END
表示所有具有DATE数据类型的列。
但错误仍然存在。这是完整的错误消息。
System.ArgumentOutOfRangeException was caught
HResult=-2146233086
Message=Hour, Minute, and Second parameters describe an un-representable DateTime.
Source=mscorlib
StackTrace:
at System.DateTime.TimeToTicks(Int32 hour, Int32 minute, Int32 second)
at Oracle.DataAccess.Client.OracleDataReader.GetDateTime(Int32 i)
at Oracle.DataAccess.Client.OracleDataReader.GetValue(Int32 i)
at System.Data.SqlClient.SqlBulkCopy.GetValueFromSourceRow(Int32 destRowIndex, Boolean& isSqlType, Boolean& isDataFeed, Boolean& isNull)
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(IDataReader reader)
从错误消息中可以看出违规部分是OracleDataReader而不是SqlBulkCopy。
如何使用Oracle查询快速发现这些违规值? 还有其他建议吗?
答案 0 :(得分:1)
Oracle数据库可以存储朱利安时代的日期,范围从公元前4712年1月1日到9999年12月31日(公共时代,或“公元”)。除非专门使用BCE(格式掩码中的'BC'),否则CE日期条目是默认值。
SQL Server的datetime
无法做到这一点。建议datetime2
用于新开发,它可以包含所有实际的日期和时间值。如果仍然达到任何范围限制,请运行样式SELECT * FROM T WHERE SomeDateCol < '0000-01-01'
的Oracle查询以查找无效数据。
TL; DR:研究确切的supported value ranges并找到任何无法映射的值。
您的问题:
NULL
。你的选择。答案 1 :(得分:0)
行。我明白了。我正在回答第2和第3个问题。
这是甲骨文的糟糕日期,看起来像这个'01/26/2006 17:94:00'。
To_char(my_column,'hh24:mi:ss')显示'00:00:00' To_char(my_column,'mi')显示'00'
它显示为有效数据,并且使用to_char()函数作为过滤器
无法识别为无效我能做的是使用转储功能
DELETE FROM my_table
WHERE my_column IS NOT NULL
AND (To_Number(SubStr(Dump(my_column), InStr(Dump(my_column),':',1,1)+2, InStr(Dump(my_column),',',1,1)-InStr(Dump(my_column),':',1,1)-2))-100 < 0
OR To_Number(SubStr(Dump(my_column), InStr(Dump(my_column),',',1,2)+1, InStr(Dump(my_column),',',1,3)-1-InStr(Dump(my_column),',',1,2))) NOT BETWEEN 1 AND 12
OR To_Number(SubStr(Dump(my_column), InStr(Dump(my_column),',',1,3)+1, InStr(Dump(my_column),',',1,4)-1-InStr(Dump(my_column),',',1,3))) NOT BETWEEN 1 AND 31
OR To_Number(SubStr(Dump(my_column), InStr(Dump(my_column),',',1,4)+1, InStr(Dump(my_column),',',1,5)-1-InStr(Dump(my_column),',',1,4))) NOT BETWEEN 1 AND 24
OR To_Number(SubStr(Dump(my_column), InStr(Dump(my_column),',',1,5)+1, InStr(Dump(my_column),',',1,6)-1-InStr(Dump(my_column),',',1,5))) NOT BETWEEN 1 AND 60
OR To_Number(SubStr(Dump(my_column), InStr(Dump(my_column),',',-1)+1)) NOT BETWEEN 1 AND 60)
这可以清理坏数据。
答案 2 :(得分:0)
“usr”的回答很好地描述了这个问题。您可以在源Oracle源列上执行CASE语句,并将问题日期转换为NULL或默认值。现在,识别出问题的行或列是一个大问题。我想出了一种识别问题的方法。
请阅读我的博客文章以确定问题列,以便您可以执行相应的DECODE将问题日期转换为NULL或有效默认值 https://sqljana.wordpress.com/tag/datetime-odp-net-oracle/