簡體   English   中英

C#中的SQL批量插入未插入值

[英]SQL Bulk Insert in C# not inserting values

我是C#的新手,所以我確定我會收到很多有關我的代碼格式的評論-我歡迎他們。 請隨時提出您的建議或建設性批評。

我正在構建一個非常簡單的Windows Form應用程序,該應用程序最終應從大小可能不同的Excel文件中獲取數據(每天可能有幾次),並將其插入SQL Server 2005中的表中。此后,在數據庫中存儲了一個過程接管執行各種更新和插入任務,具體取決於插入此表中的值。

由於這個原因,我決定使用SQL批量插入方法,因為我不知道用戶在任何給定的執行情況下是否只會插入10行或10,000行。

我正在使用的功能如下所示:

public void BulkImportFromExcel(string excelFilePath)
{
    excelApp = new Excel.Application();
    excelBook = excelApp.Workbooks.Open(excelFilePath);
    excelSheet = excelBook.Worksheets.get_Item(sheetName);
    excelRange = excelSheet.UsedRange;
    excelBook.Close(0);
    try
    {
        using (SqlConnection sqlConn = new SqlConnection())
        {
            sqlConn.ConnectionString =
            "Data Source=" + serverName + ";" +
            "Initial Catalog=" + dbName + ";" +
            "User id=" + dbUserName + ";" +
            "Password=" + dbPassword + ";";
            using (OleDbConnection excelConn = new OleDbConnection())
            {
                excelQuery = "SELECT InvLakNo FROM [" + sheetName + "$]";
                excelConn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + excelFilePath + ";Extended Properties='Excel 8.0;HDR=Yes'";
                excelConn.Open();
                using (OleDbCommand oleDBCmd = new OleDbCommand(excelQuery, excelConn))
                {
                    OleDbDataReader dataReader = oleDBCmd.ExecuteReader();
                    using (SqlBulkCopy bulkImport = new SqlBulkCopy(sqlConn.ConnectionString))
                    {
                        bulkImport.DestinationTableName = sqlTable;
                        SqlBulkCopyColumnMapping InvLakNo = new SqlBulkCopyColumnMapping("InvLakNo", "InvLakNo");
                        bulkImport.ColumnMappings.Add(InvLakNo);
                        sqlQuery = "IF OBJECT_ID('ImportFromExcel') IS NOT NULL BEGIN SELECT * INTO [" + DateTime.Now.ToString().Replace(" ", "_") + "_ImportFromExcel] FROM ImportFromExcel; DROP TABLE ImportFromExcel; END CREATE TABLE ImportFromExcel (InvLakNo INT);";
                        using (SqlCommand sqlCmd = new SqlCommand(sqlQuery, sqlConn))
                        {
                            sqlConn.Open();
                            sqlCmd.ExecuteNonQuery();
                            while (dataReader.Read())
                            {
                                bulkImport.WriteToServer(dataReader);
                            }
                        }
                    }
                }
            }
        }
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
    finally
    {
        excelApp.Quit();
    }
}

該函數運行時沒有錯誤或警告,如果我用手動SQL命令替換了WriteToServer ,則會插入行; 但是bulkImport沒有插入任何內容。

注意:在此示例中,只有一個字段,而在我當前正在運行的實際功能中進行測試; 但是最后將插入數十個字段,我將為所有這些字段做一個ColumnMapping

另外,如上所述,我知道我的代碼可能很可怕-請隨時給我您認為有幫助的任何指針。 我准備好並且願意學習。

謝謝!

如果我評論您的代碼並在同一條消息中提供指針示例代碼,我認為這將是一個非常冗長且混亂的答案,因此我決定將其分為兩部分。 首先評論:

您正在使用自動化來獲得什么? 正如我所看到的,您已經具有工作表名稱,更糟糕的是,您在最后執行app.Quit()。 完全刪除該自動化代碼。 如果您需要來自excel的一些信息(例如工作表名稱,列名稱),則可以使用OleDbConnecton的GetOleDbSchemaTable方法。 您基本上可以通過兩種方式進行映射:

  1. Excel列與SQL表列名稱的序號
  2. Excel列名稱到SQL表列名稱

兩者都會做。 在通用代碼中,假設兩個源中的列名相同 ,但是序號和計數可能不同,則可以從OleDbConnection架構表中獲取列名並在循環中進行映射。

您要刪除並創建一個名為“ ImportFromExcel”的表以用於臨時數據插入,那么為什么不通過在表名中使用#前綴簡單地創建一個臨時 SQL Server表呢? OTOH那段代碼有點奇怪,如果存在,它將從“ ImportFromExcel”進行導入,然后拖放並創建一個新的,並嘗試將批量導入到該新的。 在第一次運行中,SqlBulkCopy(SBC)將填充ImportFromExcel,在下次運行時,它將被復制到名為(DateTime.Now ...)的表中,然后通過放置清空並再次創建。 順便說一句,命名:

DateTime.Now.ToString().Replace(" ", "_") + "_ImportFromExcel"

感覺不對。 雖然看起來很誘人,但它不是可排序的,可能您想要這樣的東西:

DateTime.Now.ToString("yyyyMMddHHmmss") + "_ImportFromExcel"

或者更好:

"ImportFromExcel_" +DateTime.Now.ToString("yyyyMMddHHmmss")

因此,由於某種原因,對於所有導入,您都可以將它們排序並選擇為通配符或循環。

然后,您將在reader.Read()循環內寫入服務器。 這不是WriteToServer的工作方式。 您不會做reader.Read(),而只是:

sbc.WriteToServer(reader);

在我的下一條消息e中,我將簡單地讀取模式並將簡單的SBC示例從excel導入到臨時表中,並建議您應該如何做。

WriteToServer(IDataReader)用於內部執行IDataReader.Read()操作。

using (SqlCommand sqlCmd = new SqlCommand(sqlQuery, sqlConn))
{
    sqlConn.Open();
    sqlCmd.ExecuteNonQuery();
    bulkImport.WriteToServer(dataReader);
}

您可以檢查該函數的MSDN文檔,並提供有效的示例: https : //msdn.microsoft.com/zh-cn/library/434atets(v=vs.110).aspx

這是從Excel讀取架構信息的示例(在這里我們讀取表名-其中包含表的工作表名稱):

private IEnumerable<string> GetTablesFromExcel(string dataSource)
{
    IEnumerable<string> tables;
    using (OleDbConnection con = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" +
    string.Format("Data Source={0};", dataSource) +
    "Extended Properties=\"Excel 12.0;HDR=Yes\""))
    {
        con.Open();
        var schemaTable = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
        tables = schemaTable.AsEnumerable().Select(t => t.Field<string>("TABLE_NAME")); 
        con.Close();
    }
    return tables;
}

這是一個將excel中的SBC轉換為臨時表的示例:

void Main()
{
  string sqlConnectionString = @"server=.\SQLExpress;Trusted_Connection=yes;Database=Test";

  string path = @"C:\Users\Cetin\Documents\ExcelFill.xlsx"; // sample excel sheet
  string sheetName = "Sheet1$";

  using (OleDbConnection cn = new OleDbConnection(
    "Provider=Microsoft.ACE.OLEDB.12.0;Data Source="+path+
    ";Extended Properties=\"Excel 8.0;HDR=Yes\""))

  using (SqlConnection scn = new SqlConnection( sqlConnectionString ))
  {

    scn.Open();
    // create temp SQL server table
    new SqlCommand(@"create table #ExcelData 
    (
      [Id] int, 
      [Barkod] varchar(20)
    )", scn).ExecuteNonQuery();

    // get data from Excel and write to server via SBC  
    OleDbCommand cmd = new OleDbCommand(String.Format("select * from [{0}]",sheetName), cn);
    SqlBulkCopy sbc = new SqlBulkCopy(scn);

    // Mapping sample using column ordinals
    sbc.ColumnMappings.Add(0,"[Id]");
    sbc.ColumnMappings.Add(1,"[Barkod]");

    cn.Open();
    OleDbDataReader rdr = cmd.ExecuteReader();
    // SqlBulkCopy properties
    sbc.DestinationTableName = "#ExcelData";
    // write to server via reader
    sbc.WriteToServer(rdr);
    if (!rdr.IsClosed) { rdr.Close(); }
    cn.Close();

    // Excel data is now in SQL server temp table
    // It might be used to do any internal insert/update 
    // i.e.: Select into myTable+DateTime.Now
    new SqlCommand(string.Format(@"select * into [{0}] 
                from [#ExcelData]", 
                "ImportFromExcel_" +DateTime.Now.ToString("yyyyMMddHHmmss")),scn)
        .ExecuteNonQuery();
    scn.Close();
  }
}

從長遠來看,這可能行得通,但您需要列名,並且它們的類型可能不同,但是使用SBC進行此操作可能是一個過大的選擇,而您可以直接從MS SQL Server的OpenQuery進行操作:

SELECT * into ... from OpenQuery(...)  

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM