12

我正在使用 OleDbConnection 查询 Excel 2007 电子表格。我想强制 OleDbDataReader 仅使用字符串作为列数据类型。

系统正在查看前 8 行数据并推断数据类型为 Double。问题是,在第 9 行,我在该列中有一个字符串,并且 OleDbDataReader 返回 Null 值,因为它无法转换为 Double。

我使用了这些连接字符串:

Provider=Microsoft.ACE.OLEDB.12.0;Data Source="ExcelFile.xlsx";Persist Security Info=False;Extended Properties="Excel 12.0;IMEX=1;HDR=No"

Provider=Microsoft.Jet.OLEDB.4.0;Data Source="ExcelFile.xlsx";Persist Security Info=False;Extended Properties="Excel 8.0;HDR=No;IMEX=1"

查看 reader.GetSchemaTable().Rows[7].ItemArray[5],它的 dataType 是 Double。

此架构中的第 7 行与我遇到问题的 Excel 中的特定列相关。ItemArray[5] 是它的 DataType 列

是否可以为阅读器创建自定义 TableSchema,以便在访问 ExcelFiles 时,我可以将所有单元格视为文本,而不是让系统尝试推断数据类型?


我在这个页面找到了一些很好的信息:使用 ADO.NET 阅读 Excel 电子表格的提示

ADO.NET 接口的主要特点是如何处理数据类型。(您会注意到,在阅读电子表格时,我一直在小心避免返回哪些数据类型的问题。)您准备好了吗?ADO.NET 扫描前 8 行数据,并据此猜测每列的数据类型。然后它会尝试将该列中的所有数据强制转换为该数据类型,只要强制失败就返回 NULL!

谢谢你,
基思


这是我的代码的简化版本:

using (OleDbConnection connection = new OleDbConnection(BuildConnectionString(dataMapper).ToString()))
{
    connection.Open();
    using (OleDbCommand cmd = new OleDbCommand())
    {
        cmd.Connection = connection;
        cmd.CommandText = SELECT * from [Sheet1$];
        using (OleDbDataReader reader = cmd.ExecuteReader())
        {
            using (DataTable dataTable = new DataTable("TestTable"))
            {
                dataTable.Load(reader);
                base.SourceDataSet.Tables.Add(dataTable);
            }
        }
    }
}
4

4 回答 4

7

正如您所发现的,OLEDB 使用 Jet,它的调整方式受到限制。如果您设置为使用 OleDbConnection 从 Excel 文件中读取,则需要将该HKLM\...\Microsoft\Jet\4.0\Engines\Excel\TypeGuessRows值设置为零,以便系统扫描整个结果集。

也就是说,如果您愿意使用替代引擎从 Excel 文件中读取数据,则可以考虑尝试使用ExcelDataReader。它将所有列作为字符串读取,但允许您使用 dataReader.Getxxx 方法获取键入的值。这是一个填充 a 的示例DataSet

DataSet result;
const string path = @"....\Test.xlsx";
using ( var fileStream = new FileStream( path, FileMode.Open, FileAccess.Read ) )
{
    using ( var excelReader = ExcelReaderFactory.CreateOpenXmlReader( fileStream ) )
    {
        excelReader.IsFirstRowAsColumnNames = true;
        result = excelReader.AsDataSet();
    }
}
于 2010-04-08T03:59:27.570 回答
2

对于 64 位操作系统,请注意:

My Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Jet\4.0\Engines\Excel
于 2012-12-14T22:40:24.330 回答
1

查看此页面上的最终答案。


刚刚注意到您所指的页面说了同样的话......


更新

问题似乎出在 JET 引擎本身而不是 ADO。一旦 JET 决定了类型,它就会坚持下去。之后所做的任何事情都没有效果;就像将值转换为 SQL 中的字符串(例如 Cstr([Column]))只会导致返回一个空字符串。

此时(如果没有其他答案)我会选择其他方法:修改电子表格;修改注册表(不理想,因为您将弄乱使用 JET 的所有其他应用程序的设置);Excel 自动化或不使用 JET 的第三方组件。

如果自动化选项变慢,那么也许只需使用它以更易于处理的不同格式保存电子表格。

于 2010-04-05T18:34:48.553 回答
0

我遇到了同样的问题,并确定这是许多人普遍遇到的问题。以下是一些建议的解决方案,其中许多我已尝试实施:


  1. 将以下内容添加到您的连接字符串(Source):

TypeGuessRows=0;ImportMixedTypes=文本

  1. 将以下内容添加到您的连接字符串(来源更多讨论甚至更多):

IMEX=1;HDR=否;

  1. 编辑以下注册表设置,禁用“TypeGuessRows”,并将“ImportMixedTypes”设置为“Text”(来源不推荐更多文档):

Hkey_Local_Machine/Software/Microsoft/Jet/4.0/Engines/Excel/TypeGuessRows Hkey_Local_Machine/Software/Microsoft/Jet/4.0/Engines/Excel/ImportMixedTypes

  1. 考虑使用替代库来读取 excel 文件:

  2. 将源文件中的所有数据格式化为文本(至少前 8 行),虽然我知道这通常是不切实际的(,虽然这与 SSIS 相关,但它是相同的概念)

  3. 在导入文件之前使用 Schema.ini 文件定义数据类型,我发现这与直接使用“Jet.OleDb”有关,可能需要您修改连接字符串。这可能只适用于 CSV 我没有尝试过这种方法。(来源相关文章


这些都没有为我工作(尽管我相信他们为其他人工作)。我认为@Asher 表达的观点是,这个问题确实没有好的解决方案。在我的软件中,我只是向用户显示一条错误消息(如果任何必需的列包含空值),指示他们将所有列格式化为 "Text"

老实说,我认为这本书更适用于情况。已经多次说明的问题是:

  • “目的地的数据类型是 varchar,但假定的数据类型“double”会取消任何不适合的数据。”(来源

  • “但问题实际上出在 OLEDBDataReader 上。问题在于,如果它在列中看到的主要是数字,它会假定所有内容都是数字 - 如果正在读取的行项不是数字,它只是将其设置为 null!哎呀! “(来源

  • “问题似乎出在 JET 引擎本身,而不是 ADO。一旦 JET 决定了类型,它就会坚持下去。”(@Asher)

虽然我没有发现任何以官方身份记录的内容,但我认为很明显这是一个有意的设计决策,并且只是Jet 数据库库的工作原理。我毫不犹豫地称这个库完全没用,因为我认为对于许多人来说,其中一些解决方案确实有效,但到目前为止,对于我的项目,我得出的结论是,这个库不能在单个列中读取多种数据类型并且不适合用于一般数据检索。

于 2017-01-27T20:20:18.327 回答