使用SqlBulkCopy.WriteToServer()时如何忽略重复与NULL值?
问题场景与现有代码
1. 数据库连接方法
private void connection() sqlconn = ConfigurationManager.ConnectionStrings["SqlCom"].ConnectionString; con = new SqlConnection(sqlconn);
2. CSV数据批量插入方法
private void InsertCSVRecords(DataTable csvdt) { connection(); // 创建SqlBulkCopy对象 SqlBulkCopy objbulk = new SqlBulkCopy(con); // 指定目标表名 objbulk.DestinationTableName = "Users"; // 映射表列 objbulk.ColumnMappings.Add("Name", "Name"); objbulk.ColumnMappings.Add("Email", "Email"); // 插入数据到数据库 con.Open(); objbulk.WriteToServer(csvdt); con.Close(); }
3. 从CSV文件读取数据到DataTable
protected void Button1_Click(object sender, EventArgs e) { DataTable tblcsv = new DataTable(); // 创建列 tblcsv.Columns.Add("Name"); tblcsv.Columns.Add("Email"); string CSVFilePath = Path.GetFullPath(FileUpload1.PostedFile.FileName); string ReadCSV = File.ReadAllText(CSVFilePath); foreach (string csvRow in ReadCSV.Split('\n')) { if (!string.IsNullOrEmpty(csvRow)) { // 将每行添加到DataTable tblcsv.Rows.Add(); int count = 0; foreach (string FileRec in csvRow.Split(',')) { tblcsv.Rows[tblcsv.Rows.Count - 1][count] = FileRec; count++; } } } // 调用插入方法 InsertCSVRecords(tblcsv); }
4. 创建带主键的表的尝试
protected void CreateT_Click(object sender, EventArgs e) { string Users = ""; string Email = ""; string Name = ""; connection(); SqlCommand command = new SqlCommand(); command.Connection = con; command.CommandText = "CREATE TABLE " + Users + "(" + Email + " varchar(255), " + Name + " varchar(255), PRIMARY KEY (Email));"; con.Open(); command.ExecuteNonQuery(); con.Close(); }
核心问题
使用objbulk.WriteToServer(csvdt);批量插入时,无法忽略重复值;设置Email为主键后,插入重复值会直接抛出异常。需求是:将CSV中的数据插入表时,自动忽略重复的Email值以及NULL值。
解决方案
方案一:提前在DataTable中过滤无效数据
在调用批量插入方法前,对读取到的DataTable进行清洗,去掉Email为空/NULL的记录,同时去重:
// 过滤Email为空或NULL的记录,同时按Email去重 DataTable cleanedDt = tblcsv.AsEnumerable() .Where(row => !string.IsNullOrEmpty(row.Field<string>("Email")?.Trim())) .GroupBy(row => row.Field<string>("Email").Trim()) .Select(g => g.First()) .CopyToDataTable(); // 调用插入方法,传入清洗后的DataTable InsertCSVRecords(cleanedDt);
方案二:使用临时表+MERGE语句(适合大数据量场景)
如果CSV数据量较大,内存过滤效率低,可以先批量插入临时表,再通过SQL的MERGE语句同步有效数据到正式表,自动忽略重复值:
private void InsertCSVRecords(DataTable csvdt) { connection(); // 创建临时表(如果不存在) using (SqlCommand cmd = new SqlCommand(@" IF OBJECT_ID('tempdb..#TempUsers') IS NOT NULL DROP TABLE #TempUsers; CREATE TABLE #TempUsers ( Name varchar(255), Email varchar(255) );", con)) { con.Open(); cmd.ExecuteNonQuery(); } // 批量插入到临时表 using (SqlBulkCopy objbulk = new SqlBulkCopy(con)) { objbulk.DestinationTableName = "#TempUsers"; objbulk.ColumnMappings.Add("Name", "Name"); objbulk.ColumnMappings.Add("Email", "Email"); objbulk.WriteToServer(csvdt); } // 用MERGE同步数据,忽略重复和无效Email using (SqlCommand cmd = new SqlCommand(@" MERGE INTO Users AS Target USING ( SELECT Name, Email FROM #TempUsers WHERE Email IS NOT NULL AND LTRIM(RTRIM(Email)) != '' ) AS Source ON Target.Email = Source.Email WHEN NOT MATCHED THEN INSERT (Name, Email) VALUES (Source.Name, Source.Email);", con)) { cmd.ExecuteNonQuery(); } con.Close(); }
补充:修正建表方法的错误
当前建表代码中变量为空字符串,会导致SQL语句无效,修正后:
protected void CreateT_Click(object sender, EventArgs e) { connection(); using (SqlCommand command = new SqlCommand(@" CREATE TABLE Users( Name varchar(255), Email varchar(255) NOT NULL, PRIMARY KEY (Email) );", con)) { con.Open(); command.ExecuteNonQuery(); con.Close(); } }
内容的提问来源于stack exchange,提问作者Valeso
相关产品推荐
相关产品推荐

