[英]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方法。 您基本上可以通過兩種方式進行映射:
兩者都會做。 在通用代碼中,假設兩個源中的列名相同 ,但是序號和計數可能不同,則可以從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.