2012-05-14 75 views
2

我碰到說明今天來使用這個網站上的OleDbConnection從Microsoft Excel文件中讀取數據:OLE DB Providers插入數據表到Excel中使用Microsoft Access數據庫引擎通過OleDb的

這讓我在從所有數據讀Excel文件:

private const string EXCEL_CON = 
    @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};" + 
    @"Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'"; 

    public DataTable ExtractExcel(string fullFilename, string tableName) 
    { 
     var table = new DataTable(); 
     string strCon = string.Format(EXCEL_CON, fullFilename); 
     using (var xlConn = new System.Data.OleDb.OleDbConnection(strCon)) 
     { 
      ConnectionState initialState = xlConn.State; 
      try 
      { 
       if ((initialState & ConnectionState.Open) != ConnectionState.Open) 
       { 
        xlConn.Open(); 
       } 
       string sql = string.Format("SELECT * FROM `{0}`;", tableName); 
       using (var cmd = new System.Data.OleDb.OleDbCommand(sql, xlConn)) 
       { 
        table.Load(cmd.ExecuteReader()); 
       } 
      } 
      finally 
      { // it seems like Access does not always close the connection 
       if ((initialState & ConnectionState.Open) != ConnectionState.Open) 
       { 
        xlConn.Close(); 
       } 
      } 
     } 
     return table; 
    } 

當我插入數據,第1步是吹走在案件列Microsoft Access數據庫中現有的表已被添加,更改或刪除:

public void InsertExcel(OleDbConnection dbConn, DataTable table) { 
    ConnectionState initState = dbConn.State; 
    try { 
    if ((initState & ConnectionState.Open) != ConnectionState.Open) { 
     dbConn.Open(); 
    } 
    string sql = string.Format("SELECT * FROM {0};", table.TableName); 
    DataTable original = new DataTable(); 
    using (OleDbCommand cmd = new OleDbCommand(sql, dbConn)) { 
     try { 
     original.Load(cmd.ExecuteReader()); 
     } catch (Exception) { // table does not exist 
     } 
    } 
    if (0 < original.Rows.Count) { 
     sql = string.Format("DROP TABLE {0};", table.TableName); 
     using (OleDbCommand cmd = new OleDbCommand(sql, dbConn)) { 
     cmd.ExecuteNonQuery(); 
     } 
    } 
    // **************** 
    // CODE NEEDED HERE 
    // **************** 
    } finally { 
    if ((initState & ConnectionState.Open) != ConnectionState.Open) { 
     dbConn.Close(); 
    } 
    } 
} 

執行DROP TABLE命令後(在CODE NEEDED HERE部分),我需要以某種方式在DataTable中插入信息。

如果我沒有任何種類的主鍵,列名或列數據類型,我將如何插入表?

OleDbParameter已有AddWithValue方法,允許添加數據而無需知道數據類型。 是否有類似的東西可以用於轉儲整個DataTable(或DataSet)?

+0

可以在SELECT * INTO NewTable FROM [Excel 8.0; HDR = YES; IMEX = 1; DATABASE = Z:\ Docs \ Book1.xlsm]行上運行查詢[Sheet1 $] – Fionnuala

+0

爲了清晰起見,我更新了標題。我希望你不介意......一切順利。 – MoonKnight

+0

@Remou:這是一段簡潔的代碼,可能工作的很好,但我們正試圖保持這個類的模塊化。一種方法是從文件位置提取數據並將其作爲DataSet(多張)或DataTable返回。另一種方法(這一個)從數據中插入數據。 – jp2code

回答

9

這是一個static class我已經通過合併我已經發現或開發的各種代碼段構建。這個主要的方法是你需要注意的是ExportToExcelOleDb,其中DataSet和一個連接字符串將把DataSet寫入你選擇的Excel文件,格式爲DataSet

請注意,Access Engine寫入Excel的方式存在一個「錯誤」 - 寫入Excel工作簿時無法保留數據類型,這意味着所有數據類型均以Excel形式寫入TEXT/STRING 。無論如何,這是...

// Structures used for conversion between data-types. 
    private struct ExcelDataTypes 
    { 
     public const string NUMBER = "NUMBER"; 
     public const string DATETIME = "DATETIME"; 
     public const string TEXT = "TEXT"; // also works with "STRING". 
    } 

    private struct NETDataTypes 
    { 
     public const string SHORT = "int16"; 
     public const string INT = "int32"; 
     public const string LONG = "int64"; 
     public const string STRING = "string"; 
     public const string DATE = "DateTime"; 
     public const string BOOL = "Boolean"; 
     public const string DECIMAL = "decimal"; 
     public const string DOUBLE = "double"; 
     public const string FLOAT = "float"; 
    } 

    /// <summary> 
    /// Routine to export a given DataSet to Excel. For each DataTable contained 
    /// in the DataSet the overloaded routine will create a new Excel sheet based 
    /// upon the currently selected DataTable. The proceedure loops through all 
    /// DataRows in the selected DataTable and pushes each one to the specified 
    /// Excel file using ADO.NET and the Access Database Engine (Excel is not a 
    /// prerequisit). 
    /// </summary> 
    /// <param name="dataSet">The DataSet to be written to Excel.</param> 
    /// <param name="connectionString">The connection string.</param> 
    /// <param name="fileName">The Excel file name to export to.</param> 
    /// <param name="deleteExistFile">Delete existing file?</param> 
    public static void ExportToExcelOleDb(DataSet dataSet, string connectionString, 
                 string fileName, bool deleteExistFile) 
    { 
     // Support for existing file overwrite. 
     if (deleteExistFile && File.Exists(fileName)) 
      File.Delete(fileName); 
     ExportToExcelOleDb(dataSet, connectionString, fileName); 
    } 

    /// <summary> 
    /// Overloaded version of the above. 
    /// </summary> 
    /// <param name="dataSet">The DataSet to be written to Excel.</param> 
    /// <param name="connectionString">The SqlConnection string.</param> 
    /// <param name="fileName">The Excel file name to export to.</param> 
    public static bool ExportToExcelOleDb(DataSet dataSet, string connectionString, string fileName) 
    { 
     try 
     { 
      // Check for null set. 
      if (dataSet != null && dataSet.Tables.Count > 0) 
      { 
       using (OleDbConnection connection = new OleDbConnection(String.Format(connectionString, fileName))) 
       { 
        // Initialise SqlCommand and open. 
        OleDbCommand command = null; 
        connection.Open(); 

        // Loop through DataTables. 
        foreach (DataTable dt in dataSet.Tables) 
        { 
         // Build the Excel create table command. 
         string strCreateTableStruct = BuildCreateTableCommand(dt); 
         if (String.IsNullOrEmpty(strCreateTableStruct)) 
          return false; 
         command = new OleDbCommand(strCreateTableStruct, connection); 
         command.ExecuteNonQuery(); 

         // Puch each row into Excel. 
         for (int rowIndex = 0; rowIndex < dt.Rows.Count; rowIndex++) 
         { 
          command = new OleDbCommand(BuildInsertCommand(dt, rowIndex), connection); 
          command.ExecuteNonQuery(); 
         } 
        } 
       } 
      } 
      return true; 
     } 
     catch (Exception eX) 
     { 
      Utils.ErrMsg(eX.Message); 
      return false; 
     } 
    } 

    /// <summary> 
    /// Build the various sheet names to be inserted based upon the 
    /// number of DataTable provided in the DataSet. This is not required 
    /// for XCost purposes. Coded for completion. 
    /// </summary> 
    /// <param name="connectionString">The connection string.</param> 
    /// <returns>String array of sheet names.</returns> 
    private static string[] BuildExcelSheetNames(string connectionString) 
    { 
     // Variables. 
     DataTable dt = null; 
     string[] excelSheets = null; 

     using (OleDbConnection schemaConn = new OleDbConnection(connectionString)) 
     { 
      schemaConn.Open(); 
      dt = schemaConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); 

      // No schema found. 
      if (dt == null) 
       return null; 

      // Insert 'TABLE_NAME' to sheet name array. 
      int i = 0; 
      excelSheets = new string[dt.Rows.Count]; 
      foreach (DataRow row in dt.Rows) 
       excelSheets[i++] = row["TABLE_NAME"].ToString(); 
     } 
     return excelSheets;  
    } 

    /// <summary> 
    /// Routine to build the CREATE TABLE command. The conversion of 
    /// .NET to Excel data types is also handled here (supposedly!). 
    /// Help: http://support.microsoft.com/kb/316934/en-us. 
    /// </summary> 
    /// <param name="dataTable"></param> 
    /// <returns>The CREATE TABLE command string.</returns> 
    private static string BuildCreateTableCommand(DataTable dataTable) 
    { 
     // Get the type look-up tables. 
     StringBuilder sb = new StringBuilder(); 
     Dictionary<string, string> dataTypeList = BuildExcelDataTypes(); 

     // Check for null data set. 
     if (dataTable.Columns.Count <= 0) 
      return null; 

     // Start the command build. 
     sb.AppendFormat("CREATE TABLE [{0}] (", BuildExcelSheetName(dataTable)); 

     // Build column names and types. 
     foreach (DataColumn col in dataTable.Columns) 
     { 
      string type = ExcelDataTypes.TEXT; 
      if (dataTypeList.ContainsKey(col.DataType.Name.ToString().ToLower())) 
      { 
       type = dataTypeList[col.DataType.Name.ToString().ToLower()]; 
      } 
      sb.AppendFormat("[{0}] {1},", col.Caption.Replace(' ', '_'), type); 
     } 
     sb = sb.Replace(',', ')', sb.ToString().LastIndexOf(','), 1); 
     return sb.ToString(); 
    } 

    /// <summary> 
    /// Routine to construct the INSERT INTO command. This does not currently 
    /// work with the data type miss matches. 
    /// </summary> 
    /// <param name="dataTable"></param> 
    /// <param name="rowIndex"></param> 
    /// <returns></returns> 
    private static string BuildInsertCommand(DataTable dataTable, int rowIndex) 
    { 
     StringBuilder sb = new StringBuilder(); 

     // Remove whitespace. 
     sb.AppendFormat("INSERT INTO [{0}$](", BuildExcelSheetName(dataTable)); 
     foreach (DataColumn col in dataTable.Columns) 
      sb.AppendFormat("[{0}],", col.Caption.Replace(' ', '_')); 
     sb = sb.Replace(',', ')', sb.ToString().LastIndexOf(','), 1); 

     // Write values. 
     sb.Append("VALUES ("); 
     foreach (DataColumn col in dataTable.Columns) 
     { 
      string type = col.DataType.ToString(); 
      string strToInsert = String.Empty; 
      strToInsert = dataTable.Rows[rowIndex][col].ToString().Replace("'", "''"); 
      sb.AppendFormat("'{0}',", strToInsert); 
      //strToInsert = String.IsNullOrEmpty(strToInsert) ? "NULL" : strToInsert; 
      //String.IsNullOrEmpty(strToInsert) ? "NULL" : strToInsert); 
     } 
     sb = sb.Replace(',', ')', sb.ToString().LastIndexOf(','), 1); 
     return sb.ToString(); 
    } 

    /// <summary> 
    /// Build the Excel sheet name. 
    /// </summary> 
    /// <param name="dataTable"></param> 
    /// <returns></returns> 
    private static string BuildExcelSheetName(DataTable dataTable) 
    { 
     string retVal = dataTable.TableName; 
     if (dataTable.ExtendedProperties.ContainsKey(TABLE_NAME_PROPERTY)) 
      retVal = dataTable.ExtendedProperties[TABLE_NAME_PROPERTY].ToString(); 
     return retVal.Replace(' ', '_'); 
    } 

      /// <summary> 
    /// Dictionary for conversion between .NET data types and Excel 
    /// data types. The conversion does not currently work, so I am 
    /// puching all data upto excel as Excel "TEXT" type. 
    /// </summary> 
    /// <returns></returns> 
    private static Dictionary<string, string> BuildExcelDataTypes() 
    { 
     Dictionary<string, string> dataTypeLookUp = new Dictionary<string, string>(); 

     // I cannot get the Excel formatting correct here!? 
     dataTypeLookUp.Add(NETDataTypes.SHORT, ExcelDataTypes.NUMBER); 
     dataTypeLookUp.Add(NETDataTypes.INT, ExcelDataTypes.NUMBER); 
     dataTypeLookUp.Add(NETDataTypes.LONG, ExcelDataTypes.NUMBER); 
     dataTypeLookUp.Add(NETDataTypes.STRING, ExcelDataTypes.TEXT); 
     dataTypeLookUp.Add(NETDataTypes.DATE, ExcelDataTypes.DATETIME); 
     dataTypeLookUp.Add(NETDataTypes.BOOL, ExcelDataTypes.TEXT); 
     dataTypeLookUp.Add(NETDataTypes.DECIMAL, ExcelDataTypes.NUMBER); 
     dataTypeLookUp.Add(NETDataTypes.DOUBLE, ExcelDataTypes.NUMBER); 
     dataTypeLookUp.Add(NETDataTypes.FLOAT, ExcelDataTypes.NUMBER); 
     return dataTypeLookUp; 
    } 

我希望這對你有一些用處。

注意。我意識到這種答案令人不悅,但我花了一些時間來開發這個功能,最終沒有用到它 - 而是轉而使用COM/interop。這是一個恥辱,不要分享!

+0

Hi Killercam!這非常好。我不在乎它是不是喜歡它。但是,你說你去了COM/interop。是因爲你沒有辦法插入整數值(例如)?如果您有機會編輯並附加其他版本,我會很高興看到該版本。隨着時間的推移,我相信你會得到更多的+1票。 – jp2code

+0

切換到「Microsoft.Office.Interop.Excel」的原因是由於真正的格式化。 OleDb的方式非常快速,通過COM界面更快,但沒有像基本的數字格式那麼多 - 在美容部門是有限的。我設置用於執行COM導出的例程是多線程的,以避免UI鎖定/凍結。給我發電子郵件,我會給你的代碼... – MoonKnight

+0

我很想,但我沒有看到您的個人資料中的聯繫信息。我有我的個人資料中的聯繫信息,如果你想先給我發電子郵件:點擊我的網站鏈接,並使用聯繫表(它應該工作...)。 – jp2code

相關問題