OleDbDataReader未读取最后一条记录,Access转SQL Server数据插入异常求助
排查Access到SQL Server数据迁移最后一条记录未插入的问题
从你的代码和描述来看,核心问题大概率出在SQL语句拼接的脆弱性和错误日志的完整性上,下面是具体的排查步骤和解决方案:
一、先确认错误日志是否真的无记录
你的catch块里用了WriteToFile记录异常,但有可能出现以下情况导致最后一条的错误没被捕获到:
- 日志文件路径没有写权限,导致异常信息无法写入;
- 文件流未及时刷新,程序退出前最后一条日志没落地;
- 临时控制台输出验证:可以在catch块里加一行
Console.WriteLine,直接把错误打在控制台,确保能看到异常细节。
临时修改catch块:
catch (Exception errorException) { actionSucceedded = false; string errorMsg = $"插入失败,记录ID: {oledbReader2.GetValue(0).ToString()} | 异常信息: {errorException.Message} | 堆栈跟踪: {errorException.StackTrace}"; Console.WriteLine(errorMsg); // 临时输出到控制台,避免日志没写入的问题 WriteToFile(errorMsg); }
二、排查最后一条记录的特殊数据问题
最常见的原因是最后一条记录包含特殊字符或不符合目标表字段规则的数据:
- 比如
STRAATNAAM里有换行符、制表符这类你没处理的特殊字符,导致SQL语句语法错误; - 目标表
tblgeo_street的字段长度限制,比如streetname是varchar(100),但最后一条的街道名超过了长度; - 数值类型字段(如
city、zip)在Access中是NULL,拼接后SQL语句里出现空值(比如, ,),直接引发语法错误。
三、彻底解决:改用参数化查询(强烈推荐)
你当前用字符串拼接SQL的方式是大忌——不仅容易引发各种语法错误,还存在SQL注入风险。改用参数化查询可以完美规避这些问题,同时让错误排查更清晰:
优化后的代码示例:
OleDbCommand cmd2 = new OleDbCommand(); OleDbDataReader oledbReader2; using (cmd2 = new OleDbCommand()) { query = "SELECT ID, STRAATNAAM, 'NL' AS TAALCODE, PKANCODE, CITY FROM Temp_Unique_Streetnames WHERE TRIM(Temp_Unique_Streetnames.STRAATNAAM) <> '' AND ID > @LastId ORDER BY ID"; WriteToFile(query); cmd2.CommandText = query; cmd2.CommandType = CommandType.Text; cmd2.Connection = cn2; // 添加查询参数,避免字符串拼接隐患 cmd2.Parameters.AddWithValue("@LastId", lastId); using (oledbReader2 = cmd2.ExecuteReader()) { while (oledbReader2.Read()) { try { counter += 1; // 改用参数化插入SQL string insertQuery = @" INSERT INTO tblgeo_street (autoid, street_id, language, country, city, streetname, zip) VALUES (@AutoId, @StreetId, 'NL', 23, @City, @StreetName, @Zip) "; using (OleDbCommand cmd3 = new OleDbCommand(insertQuery, cn3)) { // 逐个添加参数,自动处理NULL和数据类型匹配 cmd3.Parameters.AddWithValue("@AutoId", counter); cmd3.Parameters.AddWithValue("@StreetId", oledbReader2.IsDBNull(0) ? DBNull.Value : oledbReader2.GetValue(0)); cmd3.Parameters.AddWithValue("@City", oledbReader2.IsDBNull(4) ? DBNull.Value : oledbReader2.GetValue(4)); cmd3.Parameters.AddWithValue("@StreetName", oledbReader2.IsDBNull(1) ? DBNull.Value : oledbReader2.GetValue(1)); cmd3.Parameters.AddWithValue("@Zip", oledbReader2.IsDBNull(3) ? DBNull.Value : oledbReader2.GetValue(3)); WriteToFile($"正在插入记录ID: {oledbReader2.GetValue(0)}"); int affectedRows = cmd3.ExecuteNonQuery(); // 额外验证是否真的插入成功 if (affectedRows == 0) { WriteToFile($"警告:记录ID {oledbReader2.GetValue(0)} 未插入任何行"); } } } catch (Exception errorException) { actionSucceedded = false; string errorMsg = $"插入失败,记录ID: {oledbReader2.IsDBNull(0) ? "NULL" : oledbReader2.GetValue(0).ToString()} | 异常信息: {errorException.Message} | 堆栈跟踪: {errorException.StackTrace}"; Console.WriteLine(errorMsg); WriteToFile(errorMsg); } } } }
四、额外排查点
- 检查目标表主键约束:比如
autoid是自增主键,但你手动传入了counter值,可能最后一条的counter值已经存在,导致主键冲突; - 查看SQL Server日志:直接登录SQL Server查看错误日志,可能会有代码层面没捕获到的数据库级错误;
- 验证读取记录数:在循环前统计实际读取的记录数,确认是否真的从Access获取到了5条记录。
内容的提问来源于stack exchange,提问作者francisp
相关产品推荐
相关产品推荐

