将excel中的数据导入多个表

时间:2010-09-08 01:44:10

标签: c# sql excel import

我正在构建一个离线C#应用程序,它将从电子表格中导入数据并将它们存储在我创建的SQL数据库中(项目内部)。通过一些研究,我已经能够使用一些可以导入静态表的代码到数据库中,该数据库与工作表中的列完全相同

我想要做的是根据名称将特定的列放到正确的表中。这样我就可以正确设计数据库而不只是有一个巨大的表来存储所有内容。

下面是我用来将一些静态字段导入一个表的代码,我希望能够将导入的数据拆分成多个。

这样做的最佳方式是什么?

public partial class Form1 : Form
    {
        string strConnection = ConfigurationManager.ConnectionStrings
        ["Test3.Properties.Settings.Test3ConnectionString"].ConnectionString;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {


            //Create connection string to Excel work book
            string excelConnectionString =
            @"Provider=Microsoft.Jet.OLEDB.4.0;
            Data Source=C:\Test.xls;
            Extended Properties=""Excel 8.0;HDR=YES;""";

            //Create Connection to Excel work book
            OleDbConnection excelConnection = new OleDbConnection(excelConnectionString);

            //Create OleDbCommand to fetch data from Excel
            OleDbCommand cmd = new OleDbCommand
            ("Select [Failure_ID], [Failure_Name], [Failure_Date], [File_Name], [Report_Name], [Report_Description], [Error] from [Failures$]", excelConnection);

            excelConnection.Open();
            OleDbDataReader dReader;
            dReader = cmd.ExecuteReader();

            SqlBulkCopy sqlBulk = new SqlBulkCopy(strConnection);
            sqlBulk.DestinationTableName = "Failures";
            sqlBulk.WriteToServer(dReader);

        }

4 个答案:

答案 0 :(得分:2)

您可以尝试ETL(提取 - 转换 - 加载)架构:

Extract:一个类将打开文件并获取您知道如何使用的块中的所有数据(通常从文件中取一行并将其数据解析为包含保存相关数据的字段的POCO对象),并将这些放入其他工作流程可以接受的队列中。在这种情况下,您可能要做的第一件事就是让Excel打开文件并将其重新保存为CSV,这样您就可以在过程中将其重新打开为基本文本并有效地进行切割。您还可以阅读列名并构建“映射字典”;此列被命名为,因此它将转到数据对象的此属性。这个过程应该尽可能快地发生,并且它应该失败的唯一原因是因为在给定文件结构的情况下,行的格式与您要查找的格式不匹配。

转换:将文件的内容提取到基本行的实例后,执行将文件中的行转换为符合您的域模型的一组域对象所需的任何验证,计算或其他业务规则。这个过程可能会像您需要的那样复杂,但同样应该尽可能简单,同时遵守您的要求中给出的所有业务规则。

加载:现在您在自己的域对象中有了一个对象图,您可以使用您调用的相同持久性框架来处理以任何其他方式创建的域对象。这可能是基本的ADO,像NHibernate或MSEF的ORM,或者是对象知道如何自我保持的Active Record模式。它不是批量加载,但它可以节省您必须实现完全不同的持久性模型,只是为了将基于文件的数据导入数据库。

ETL工作流程可以帮助您将重复性任务分成简单的工作单元,从那里您可以识别花费大量时间并考虑并行流程的任务。

或者,在调用批量插入例程来处理数据之前,您可以通过检测要使用的列并将其排列为与批量输入规范匹配的格式来获取文件并按下其格式。此文件处理器例程可以执行您想要的任何操作,包括将数据分成多个文件。但是,它是一个大的进程,一次只能处理整个文件,并且优化或并行处理的机会有限。但是,如果你的加载机制很慢,或者你有很多很容易消化的数据,那么它甚至可能比设计良好的ETL更快。

在任何情况下,我都会尽快摆脱Office格式并转换为纯文本(或XML)格式,我绝对不会在服务器上安装Office。如果有任何方法你可以要求文件采用一些易于解析的格式,如CSV加载之前,那就更好了。在服务器上安装Office通常是一件非常糟糕的事情,而服务器应用程序中的OLE操作并不是更好。该应用程序将非常脆弱,Office希望告诉您的任何内容将导致应用程序挂起,直到您登录服务器并清除对话框。

答案 1 :(得分:0)

如果您只对文本感兴趣(不是格式化等),或者您可以将Excel文件保存为CSV文件,而是解析CSV文件,这很简单。

答案 2 :(得分:0)

根据程序的生命周期,我建议使用以下两个选项之一。

  1. 如果程序在使用中很短暂,或者通常是“扔掉”项目,我会推荐一系列例程,使用标准SQL和一些字符串处理将数据解析并输入到另一组表中根据需要。

  2. 如果程序会延长时间和/或在日常工作中找到更多用途,我建议实施类似于@KeithS推荐的解决方案。通过一组明确定义的数据处理步骤,可以获得很大的灵活性。更具体地说,.NET实体框架可能非常适合。 作为奖励,如果您还不熟悉这个领域,您可能会发现在第一次使用ORM时,您在边界之间使用数据(xls - > sql - >等)可以学到很多东西。作为EF。

答案 3 :(得分:0)

如果您正在寻找更多与代码相关的答案,您可以使用以下内容修改代码以使用困难的列名/不同的表:

    private void button1_Click(object sender, EventArgs e)
    {
        //Create connection string to Excel work book
        string excelConnectionString =
        @"Provider=Microsoft.Jet.OLEDB.4.0;
        Data Source=C:\Test.xls;
        Extended Properties=""Excel 8.0;HDR=YES;""";

        //Create Connection to Excel work book
        OleDbConnection excelConnection = new OleDbConnection(excelConnectionString);

        //Create OleDbCommand to fetch data from Excel
        OleDbCommand cmd = new OleDbCommand
        ("Select [Failure_ID], [Failure_Name], [Failure_Date], [File_Name], [Report_Name], [Report_Description], [Error] from [Failures$]", excelConnection);

        excelConnection.Open();

        DataTable dataTable = new DataTable();
        dataTable.Columns.Add("Id", typeof(System.Int32));
        dataTable.Columns.Add("Name", typeof(System.String));
        // TODO: Complete other table columns
        using(OleDbDataReader dReader = cmd.ExecuteReader())
        {
            DataRow dataRow = dataTable.NewRow();
            dataRow["Id"] = dReader.GetInt32(0);
            dataRow["Name"] = dReader.GetString(1);
            // TODO: Complete other table columns
            dataTable.Rows.Add(dataRow);
        }

        SqlBulkCopy sqlBulk = new SqlBulkCopy(strConnection);
        sqlBulk.DestinationTableName = "Failures";
        sqlBulk.WriteToServer(dataTable);
    }

现在,您可以控制列的名称以及导入数据的表。 SqlBulkCopy适合插入大量数据。如果您只有少量行,那么最好创建一个标准数据访问层来插入记录。