11

我需要访问一个 Excel 电子表格并将电子表格中的数据插入 SQL 数据库。然而,主键是混合的,大多数是数字的,有些是字母数字的。

我遇到的问题是,当数字和字母数字键位于同一个电子表格中时,字母数字单元格返回空白值,而所有其他单元格返回它们的数据没有问题。

我正在使用 OleDb 方法来访问 Excel 文件。使用命令字符串检索数据后,我将数据放入 DataAdapter,然后填充 DataSet。我遍历 DataSet 中第一个 DataTable 中的所有行 (dr)。

我使用 dr["..."].ToString() 引用列

如果我在 Visual Studio 2008 中调试项目并查看“扩展属性”,通过将鼠标悬停在“dr”上,我可以查看 DataRow 的值,但应该是字母数字的主键是 {}。其他值用引号括起来,但空白值带有大括号。

这是 C# 问题还是 Excel 问题?

有没有人以前遇到过这个问题,或者可能找到了解决方法/修复?

提前致谢。

4

10 回答 10

27

解决方案:

连接字符串:

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=FilePath;Extended Properties="Excel 8.0;HDR=Yes;IMEX=1";

  1. HDR=Yes;表示第一行包含列名,而不是数据。HDR=No;表示相反。

  2. IMEX=1;告诉驱动程序始终将“混合”(数字、日期、字符串等)数据列作为文本读取。请注意,此选项可能会对 excel 工作表写入访问产生负面影响。

SQL 语法SELECT * FROM [sheet1$]。即excel工作表名称后跟a$并用[ ]括号括起来。

重要的:

  • 查看位于注册表 REG_DWORD "TypeGuessRows" 的 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel]。这是不让 Excel 仅使用前 8 行来猜测列数据类型的关键。将此值设置为 0 以扫描所有行。这可能会损害性能。

  • 如果 Excel 工作簿受密码保护,则无法打开它进行数据访问,即使提供正确的密码和连接字符串也是如此。如果您尝试,您会收到以下错误消息:“无法解密文件”。

于 2009-08-10T04:41:57.913 回答
3

Excel 数据源为整个列选择一种列类型。如果其中一个单元格与该类型不完全匹配,则会留下这样的空白。我们遇到的问题是,我们的打字员在数字列中输入了“8”(数字前有一个空格,因此 Excel 将其转换为该单元格的字符串)。对我来说,尝试使用 .Net Parse 方法是有意义的,因为它们更健壮,但我想这不是 Excel 驱动程序的工作方式。

由于我们使用数据库导入服务,我们的解决方法是记录所有以这种方式“失败”的行。然后,我们返回 XLS 文档并重新键入这些单元格,以确保基础类型正确。(我们发现仅仅删除空格并不能解决问题——我们必须先清除整个单元格,而不是重新输入“8”。)感觉 hacky 并且不是 elagent,但这是我们找到的最佳方法。如果 Excel 驱动程序无法自行正确读取它,那么一旦您进入 .Net,您就无法从那里获取这些数据。

Office 以简单的名义向用户隐藏重要细节的另一种情况,因此在您必须准确使用电源时变得更加困难。

于 2009-03-05T16:47:36.813 回答
1

{} 表示这是某种空对象,而不是字符串。当您将鼠标悬停在对象上时,您应该能够看到它的类型。同样,当您使用 quickwatch 查看 dr["..."] 时,您应该看到对象类型。您收到的对象是什么类型的?

于 2008-11-18T14:35:42.377 回答
1

我在这里回答了一个类似的问题。为了您的方便,我在这里复制并粘贴了相同的答案:

我遇到了同样的问题,但无需借助 Excel COM 接口或第 3 方软件即可解决此问题。它涉及一些处理开销,但似乎对我有用。

  1. 首先读入数据得到列名
  2. 然后使用这些列中的每一个创建一个新的 DataSet,将它们的每个 DataTypes 设置为字符串。
  3. 再次将数据读入这个新数据集中。瞧——科学记数法现在消失了,一切都以字符串的形式读入。

这是一些说明这一点的代码,作为额外的奖励,它甚至是 StyleCopped!

public void ImportSpreadsheet(string path)
{
    string extendedProperties = "Excel 12.0;HDR=YES;IMEX=1";
    string connectionString = string.Format(
        CultureInfo.CurrentCulture,
        "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"{1}\"",
        path,
        extendedProperties);

    using (OleDbConnection connection = new OleDbConnection(connectionString))
    {
        using (OleDbCommand command = connection.CreateCommand())
        {
            command.CommandText = "SELECT * FROM [Worksheet1$]";
            connection.Open();

            using (OleDbDataAdapter adapter = new OleDbDataAdapter(command))
            using (DataSet columnDataSet = new DataSet())
            using (DataSet dataSet = new DataSet())
            {
                columnDataSet.Locale = CultureInfo.CurrentCulture;
                adapter.Fill(columnDataSet);

                if (columnDataSet.Tables.Count == 1)
                {
                    var worksheet = columnDataSet.Tables[0];

                    // Now that we have a valid worksheet read in, with column names, we can create a
                    // new DataSet with a table that has preset columns that are all of type string.
                    // This fixes a problem where the OLEDB provider is trying to guess the data types
                    // of the cells and strange data appears, such as scientific notation on some cells.
                    dataSet.Tables.Add("WorksheetData");
                    DataTable tempTable = dataSet.Tables[0];

                    foreach (DataColumn column in worksheet.Columns)
                    {
                        tempTable.Columns.Add(column.ColumnName, typeof(string));
                    }

                    adapter.Fill(dataSet, "WorksheetData");

                    if (dataSet.Tables.Count == 1)
                    {
                        worksheet = dataSet.Tables[0];

                        foreach (var row in worksheet.Rows)
                        {
                            // TODO: Consume some data.
                        }
                    }
                }
            }
        }
    }
}
于 2010-09-09T16:18:10.303 回答
1

ItemArray 是一个对象数组。所以我假设我试图引用的 DataRow 中的“列”是对象类型。

于 2008-11-19T04:48:56.743 回答
1

为了兼容 VISTA,您可以在连接字符串中使用 EXCEL 12.0 驱动程序。这应该可以解决您的问题。它做到了我的。

于 2009-02-18T15:49:51.520 回答
1

解决方案:

  1. 您输入 HDR=No 以便第一行不被视为列标题。连接字符串:Provider=Microsoft.Jet.OLEDB.4.0;Data Source=FilePath;Extended Properties="Excel 8.0;HDR=No;IMEX=1";
  2. 您忽略第一行并通过任何您想要的方式访问数据(DataTable、DataReader 等)。您通过数字索引而不是列名访问列。

它对我有用。这样您就不必修改寄存器!

于 2009-09-03T09:53:14.103 回答
1

通过 ascii 代码以降序对 xls 文件中的记录进行排序,以便字母数字字段将出现在标题行下方的顶部。这确保了读取的第一行数据将数据类型定义为“varchar”或“nvarchar”

于 2011-10-25T16:40:03.750 回答
0

这不完全正确!显然,如果前 8 行为空,则 Jet/ACE 总是假定字符串类型,而不管 IMEX=1。即使我在注册表中将行读取为 0,我仍然遇到同样的问题。这是让它工作的唯一可靠的方法:

try
{
    Console.Write(wsReader.GetDouble(j).ToString());
}
catch   //Lame unfixable bug
{
    Console.Write(wsReader.GetString(j));
}

于 2012-07-16T19:49:53.693 回答
0

嗨,所有这些代码也是获取字母数字值

using System.Data.OleDb;

string ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + filepath + ";" + "Extended Properties="+(char)34+"Excel 8.0;IMEX=1;"+(char)34;

string CommandText = "select * from [Sheet1$]";

OleDbConnection myConnection = new OleDbConnection(ConnectionString);
myConnection.Open();

OleDbDataAdapter myAdapter = new OleDbDataAdapter(CommandText, myConnection);

ds = null;
ds = new DataSet();
myAdapter.Fill(ds);
于 2011-05-13T08:48:35.433 回答