1. 程式人生 > >大資料量高效匯入資料庫(以excel匯入sqlserver為例)

大資料量高效匯入資料庫(以excel匯入sqlserver為例)

       最近正在做一個專案,要把excel中的資料匯入到sqlserver資料庫中,首先想到的就是insert,採用連線Excel物件的Microsoft.ACE.OLEDB.12.0介面引擎連線到excel,然後讀取到DataTable中然後每次讀取一個Row,insert到資料庫表裡,但是效率低的讓人可怕,一旦資料量過大,卡的要死,我們測試資料是有24萬條,檔案大小14,249,487 位元組(大概14MB),後來經過一番研究發現了SqlBulkCopy,至於SqlBulkCopy的詳細介紹就不說了,都是文字描述,微軟的直接MSDN就可以了,但是效率確實比insert效率高很多,這裡我們就直接上程式碼吧:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


namespace 讀取excel到datagridview
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
        }

        /// <summary>
        /// 選擇檔案,並且讀取excel中sheet
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {  
             try
             {
                //獲取Excel檔案路徑和名稱
                OpenFileDialog odXls = new OpenFileDialog();
                // 指定相應的開啟文件的目錄
                odXls.InitialDirectory = "C://";
                // 設定檔案格式
                odXls.Filter = "Excel files (*.xls)|*.xls|Excel files (*.xlsx)|*.xlsx";
                odXls.FilterIndex = 2;
                odXls.RestoreDirectory = true;
                if (odXls.ShowDialog() == DialogResult.OK)
                {
                   txtFilePath.Text = odXls.FileName;
                   OleDbConnection oledbConn = null;
                   string sConnString = "provider=Microsoft.ACE.OLEDB.12.0;data source=" + odXls.FileName + ";Extended Properties=Excel 12.0;Persist Security Info=False";
                   oledbConn = new OleDbConnection(sConnString);
                   oledbConn.Open();
                   DataTable dt = oledbConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
                   combox1.Items.Clear();
                   foreach (DataRow dr in dt.Rows)
                   {
                       //MessageBox.Show((String)dr["TABLE_NAME"]);
                      combox1.Items.Add((String)dr["TABLE_NAME"]);
                   }
                   if (combox1.Items.Count > 0)
                      combox1.SelectedIndex = 0;
                }
             }
             catch (Exception Ex)
             {
                 MessageBox.Show(Ex.Message);
                 richTextBox1.Text = Ex.Message;
             }    
        }

        /// <summary>
        /// 讀取檔案具體內容
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            string connectionString = @"Data Source=702-01;Initial Catalog=DBUser;Integrated Security=True";
            OleDbConnection ole = null;
            OleDbDataAdapter da = null;
            DataTable dt = null;


            string strConn = "Provider=Microsoft.ACE.OLEDB.12.0;"
                            + "Data Source=" + txtFilePath.Text.Trim() + ";"
                            + "Extended Properties=Excel 12.0";
            string sTableName = combox1.Text.Trim();
            string strExcel = "select * from [" + sTableName + "]";
            try
            {
                ole = new OleDbConnection(strConn);
                ole.Open();
                da = new OleDbDataAdapter(strExcel, ole);
                dt = new DataTable();
                da.Fill(dt);
                using (System.Data.SqlClient.SqlBulkCopy bcp = new System.Data.SqlClient.SqlBulkCopy(connectionString))
                {
                    //bcp.SqlRowsCopied += new System.Data.SqlClient.SqlRowsCopiedEventHandler(bcp_SqlRowsCopied);  
                    //bcp.SqlRowsCopied += new System.Data.SqlClient.SqlRowsCopiedEventHandler(bcp_SqlRowsCopied);
                    bcp.BatchSize = 1000;//每次傳輸的行數  
                   // bcp.NotifyAfter = 1000;//進度提示的行數  
                    bcp.DestinationTableName = "tb_bigdata";//目標表
                    bcp.WriteToServer(dt);
                    MessageBox.Show("匯入完成!");
                }
                //為datagridview設定資料來源
                this.xlsExpData.DataSource = dt;


                //設定每一列顯示資料模式為AllCells
                //for (int i = 0; i < dt.Columns.Count; i++)
                //{
                //   xlsExpData.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
                //}
                ole.Close();
            }
            catch (Exception Ex)
            {
                MessageBox.Show(Ex.Message);
            }
            finally
            {
                if (ole != null)
                    ole.Close();
            }    
        }

        //進度顯示  
        void bcp_SqlRowsCopied(object sender, System.Data.SqlClient.SqlRowsCopiedEventArgs e)
        {
            this.Text = e.RowsCopied.ToString();
            this.Update();
        }
    }
}


這裡我們經過多次測試,將24萬條資料匯入到sqlserver中,因為目前手邊沒有可以測試的伺服器就臨時使用自己的計算機當做伺服器進行測試,這裡是我的電腦配置:


平均匯入時間在20秒左右,上下不差1秒,執行時間和BatchSize設定也有關係,如果設定為100,則需要35秒左右,如果是真正的伺服器的話相信執行時間會大大縮短,大家都知道伺服器的處理事務能力比普通計算機要強大很多。

當然在本地使用時,效率是比insert高的,但是也有很多限制比如:

1、匯入時會有排它意向鎖,易死鎖。

2、資料只能從伺服器本地匯入到本地伺服器上的資料庫中,無法從客戶端將資料匯入到遠端伺服器上等等

那麼可能大家會擔心了,這樣也不行啊,客戶端不能匯入到遠端伺服器上很不方便的,那麼我又研究了一種實現的方式,就是採用資料介面卡,DataSet實現資料匯入,我們可以將資料檔案先匯入到DataTable或者DataSet中,然後提交回資料庫,這樣也實現了資料匯入,這樣就不會有上邊的限制了,至於具體程式碼就不說了,相信大家應該都會,就是ADO.NET所謂基本物件的應用,如果真的有需要具體程式碼的可以聯絡我,但是匯入過程中會瞬間部分記憶體

佔用,但是佔用的記憶體應該沒啥問題,不會有什麼影響。

所以我們可以視情況而定,具體選擇哪種匯入方式,靈活運用,提高工作效率,當然這裡由於自身能力原因,暫時只研究了這幾種方式,一定還有效率更高的匯入方式,這裡我會繼續研究,如果有新進展一定及時更新,如果有需要請關注或者私信,大家一起學習,一起進步,希望會幫助到大家。