sql - 根据单元格位置将选择性字段从 Excel 批量插入到 SQL

标签 sql sql-server excel ssis etl

我有一个 SSIS 包,我必须从 Excel 工作表中选择一些值并将它们插入到 SQL Server 数据库表中,我是通过执行 sql 任务来完成的。

步骤如下:

  1. 从映射表中选择所有记录,单元格位置是动态的,因此将其保留在 SQL 表中(大约有 3000 个单元格 - 我们必须从 Excel 中的选择性字段中选取值,而不是全部)

示例:

enter image description here

  • 在每条记录上迭代 Foreach

  • 使用单元格名称和工作表名称创建查询

    示例:从 [GenDet$F3:F3] 中选择 *

  • 执行查询以从 Excel 工作表中获取该单元格的值

  • 示例:

    enter image description here

  • 将值插入 SQL 数据库表
  • enter image description here

    它正在工作 - 但问题是它所花费的时间。对于 3000 个字段,处理一个 Excel 文件需要 50 分钟。我必须在一分钟之内完成此操作。

    请让我知道实现此目标的最佳方法。

    谢谢!

    最佳答案

    正如我在评论中提到的,我认为编写一个 C# 脚本从 Excel 单元格读取数据并将它们分组到列表或 DataTable 中,然后执行一次批量插入会更高效

    C# 应用程序/脚本任务

    所需的组件

    首先您必须导入 Excel Interop 程序集:

    using Microsoft.Office.Interop.Excel;
    using System.Data.SqlClient;
    

    将列标题字母转换为索引

    现在您应该定义以下函数,将 Excel 列字母表转换为索引:

    private int ParseColHeaderToIndex(string colAdress)
    {
        int[] digits = new int[colAdress.Length];
        for (int i = 0; i < colAdress.Length; i++)
        {
            digits[i] = Convert.ToInt32(colAdress[i]) - 64;
        }
        int mul = 1;
        int res = 0;
        for (int pos = digits.Length - 1; pos >= 0; pos--)
        {
            res += digits[pos] * mul;
            mul *= 26;
        }
        return res;
    }
    

    SQL批量插入功能

    以下函数是对SQL执行批量插入操作

    public void InsertToSQLUsingSQLBulk(System.Data.DataTable dt, string connectionstring, string Tablename)
    {
    
    
        try
        {
            using (var bulkCopy = new SqlBulkCopy(connectionstring, SqlBulkCopyOptions.KeepIdentity))
            {
    
                foreach (DataColumn col in dt.Columns)
                {
                    bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
                }
    
                bulkCopy.BulkCopyTimeout = 600;
                bulkCopy.DestinationTableName = Tablename;
                bulkCopy.WriteToServer(dt);
            }
    
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
    

    从 Excel 读取到目标 DataTable

    以下函数采用 Excel 路径和范围 DataTable 作为参数,并返回具有目标结构的 DataTable(Id, AttributeKey, AttributeValue)

    public System.Data.DataTable ReadFromExcel(System.Data.DataTable dtRanges,string strPath)
    {
    
        string num = "0123456789";
    
        //Declare result datatable  
        System.Data.DataTable destination = new System.Data.DataTable();
        destination.Columns.Add("Id");
        destination.Columns.Add("AttributeKey");
        destination.Columns.Add("AttributeValue");
    
        //Decalre Interop Objects
         Microsoft.Office.Interop.Excel.Application m_XlApp;
         m_XlApp = new Microsoft.Office.Interop.Excel.Application();
         m_XlApp.Visible = false;
         m_XlApp.DisplayAlerts = false;
    
         Workbook xlWbs = null;
         xlWbs = m_XlApp.Workbooks.Open(strPath, Type.Missing, Type.Missing, 
                                       Type.Missing, "'", Type.Missing, Type.Missing, 
                                       Type.Missing, Type.Missing, Type.Missing, 
                                       Type.Missing, Type.Missing, Type.Missing, 
                                       Type.Missing, Type.Missing);
    
        xlWbs.DoNotPromptForConvert = true;
        xlWbs.CheckCompatibility = false;
        xlWbs.Application.DisplayAlerts = false;
    
        //Loop over worksheets
        foreach (Worksheet xlWks in xlWbs.Worksheets) {
    
            string Name = xlWks.Name;
    
            //Assing rows relevant to the current sheet
    
            foreach (DataRow drRow in dtRanges.AsEnumerable().Where(x => x["Sheet_Name"].ToString() == Name))
            {
    
                string sheet = drRow["Sheet_Name"].ToString();
                string range = drRow["Location_Value"].ToString();
                string field = drRow["Field_Name"].ToString();
                string id = drRow["Id"].ToString();
                string rangeAlpha = range.Split(':')[0];
                int rowidx = 0;
                int colidx = 0;
    
    
    
                foreach (char chr in num) { 
                    rangeAlpha = rangeAlpha.Replace(chr, '\0');
                }
    
                rowidx = Int32.Parse(range.Split(':')[0].Replace(rangeAlpha, ""));
                colidx = ParseColHeaderToIndex(rangeAlpha);
    
    
                DataRow dr = destination.NewRow();
    
                if (xlWks.Cells[rowidx, colidx] != null && (xlWks.Cells[rowidx, colidx] as Range).Value2 != null)
                {
    
                    dr["AttributeValue"] = (string)(xlWks.Cells[rowidx, colidx] as Range).Value2;
                }
                else
                {
                    dr["AttributeValue"] = "";
                }
    
    
    
                dr["AttributeKey"] = drRow["Field_Name"].ToString();
                dr["Id"] = drRow["Id"].ToString();
    
                destination.Rows.Add(dr);
            }
    
        }
    
        xlWbs.Close(false, Type.Missing, Type.Missing);
        m_XlApp.Quit();
        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlWbs);
        System.Runtime.InteropServices.Marshal.ReleaseComObject(m_XlApp);
    
    
        return destination;
    
    }
    

    主程序

    public void Main(){
    
        //Initialize ranges table
        System.Data.DataTable ranges = new System.Data.DataTable();
        ranges.Columns.Add("Id");
        ranges.Columns.Add("Field_Name");
        ranges.Columns.Add("Location_Value");
        ranges.Columns.Add("Sheet_Name");
    
        //Add rows or read them from database using SQLDataAdapter
    
    
        //note that the destination table must exists in the database with identical columns of datatable
    
        System.Data.DataTable destination = ReadFromExcel(ranges, "C:\\1.xlsx", "dbo.destination");
    
        InsertToSQLUsingSQLBulk(destination, "Pass SQL Server destination connection string here");
    
    
    
    }
    
    <小时/>

    更新 1 - 提高性能

    您可以通过将所有工作表内容放入二维数组中,然后循环数组而不是在 Excel 工作表内循环来提高方法性能。

    Excel.Range targetCells = xlWks.UsedRange;
    object[,] allValues = (object[,])targetCells.Cells.Value;
    
    ...
    
    
     if (targetCells.Cells[rowidx, colidx] != null)
     {
    
         dr["AttributeValue"] = (string)(targetCells.Cells[rowidx, colidx] as Range).Value2;
      }
      else
      {
         dr["AttributeValue"] = "";
      }
    

    引用

    关于sql - 根据单元格位置将选择性字段从 Excel 批量插入到 SQL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55009040/

    相关文章:

    java - 尝试通过JAVA 500错误删除数据库行

    php - 在单个查询中查找 mysql 表中某个条目左侧和右侧的条目

    sql-server - 加入和总结多列

    c# - 如何在 SQL Server 2008 中的特定列中存储列值?

    sql - 如何与其他表同时创建一个表以从每个表中提取总计?

    r - 将列值转换为 R 中的日期

    mysql - MySQL 查询将一列引用到另一个表时出错

    sql - 在 Postgres 中返回 SETOF 表的函数

    Excel VBA - 按标题搜索列并粘贴到新工作表中

    excel - 微软 Excel : Formula in MS Excel