【问题标题】:C# - Importing a CSV where empty strings do not get sent through bulk import to databaseC# - 导入 CSV,其中空字符串不会通过批量导入发送到数据库
【发布时间】:2016-04-15 17:19:05
【问题描述】:

所以我有一个包含 27 列的站点位置信息数据库。我编写了一个批量导入功能,该功能采用 CSV 文件并允许您修改信息。当 CSV 文件在每一列中都包含信息时,一切正常,但是当有一个空字符串/null 时,它会将一个空字符串推送到数据库并清除之前的内容。

为了能够快速更新数据库中的某些属性,我的客户希望只需要输入站点的主键(我已对导入进行了编码以使其可更新)并填写任何其他属性而无需填写其余部分。例如,一个位置的 CSV 文件可能看起来像这样:

SITE32,,,,BS,11111,,43607,123566789,123456789,2.2.2.2,1.1.1.1.1,1.1.1.1,0,Test,Test,Testing,2,12123,5002,N/A,4,00201,3,000,3703,5

假设这个网站已经存在于数据库中,我只想更新填写的字段。留空的应该保持不变。

这是我目前的代码

       protected void btnBulkSite_click(object sender, EventArgs e)
            {

                if (FileUpLoad1.HasFile)
                {
                    FileUpLoad1.SaveAs(@"C:\temp\" + FileUpLoad1.FileName);
                    btnBulkSite.Text = "File Uploaded: " + FileUpLoad1.FileName;
                }
                else
                {
                    btnBulkSite.Text = "No File Uploaded.";
                }
                DataTable tblcsv = new DataTable();

                tblcsv.Columns.Add("SERVER_ID", typeof(string));
                tblcsv.Columns.Add("SITE_NAME", typeof(string));
                tblcsv.Columns.Add("SITE_ADDRESS", typeof(string));
                tblcsv.Columns.Add("SITE_CITY", typeof(string));
                tblcsv.Columns.Add("SITE_STATE", typeof(string));
                tblcsv.Columns.Add("SITE_ZIPCODE", typeof(string));
                tblcsv.Columns.Add("SITE_COUNTY", typeof(string));
                tblcsv.Columns.Add("SITE_INTERNALZIP", typeof(string));
                tblcsv.Columns.Add("SITE_PHONE_NUM", typeof(string));
                tblcsv.Columns.Add("SITE_FAX_NUM", typeof(string));
                tblcsv.Columns.Add("SERVER_SUBNET_ADDR", typeof(string));
                tblcsv.Columns.Add("SERVER_IP_ADDR", typeof(string));
                tblcsv.Columns.Add("SERVER_GATEWAY_ADDR", typeof(string));
                tblcsv.Columns.Add("COSTCENTER_NUM");
                tblcsv.Columns.Add("DCMF_NAME", typeof(string));
                tblcsv.Columns.Add("LU_ID", typeof(string));
                tblcsv.Columns.Add("XIDPU_ID", typeof(string));
                tblcsv.Columns.Add("TRAININGSITE_IND");
                tblcsv.Columns.Add("PBA_FICS_NUM");
                tblcsv.Columns.Add("PBA_CITY_ID", typeof(string));
                tblcsv.Columns.Add("REGION_NAME", typeof(string));
                tblcsv.Columns.Add("SITETYPE_ID");
                tblcsv.Columns.Add("PBA_OFFICE_ID", typeof(string));
                tblcsv.Columns.Add("SITEORIGIN_ID");
                tblcsv.Columns.Add("REGION_ID", typeof(string));
                tblcsv.Columns.Add("PBA_BANK_ID", typeof(string));
                tblcsv.Columns.Add("SITE_REGION_ID");



                System.IO.StreamReader stream = new System.IO.StreamReader(FileUpLoad1.PostedFile.InputStream);

                string ReadCSV = stream.ReadToEnd();
                foreach (string csvRow in ReadCSV.Split('\n'))
                    {

                        if (!string.IsNullOrEmpty(csvRow))
                        {
                        tblcsv.Rows.Add();
                        int count = 0;

                        foreach (string FileRec in csvRow.Split(','))
                        {
                            tblcsv.Rows[tblcsv.Rows.Count - 1][count] = FileRec;

                            count++;
                        }
                    }
                }

                RemoveAllNullColumnsFromDataTable(tblcsv);

            }

  public void RemoveAllNullColumnsFromDataTable(DataTable tblcsv)
        {
            for (int h = 0; h < tblcsv.Rows.Count; h++)
            {
                if (tblcsv.Rows[h].IsNull(0) == true)
                {
                    tblcsv.Rows[h].Delete();
                }

            }
            tblcsv.AcceptChanges();
            foreach (var column in tblcsv.Columns.Cast<DataColumn>().ToArray())
            {
                if (tblcsv.AsEnumerable().All(dr => dr.IsNull(column)))
                    tblcsv.Columns.Remove(column);
            }
            tblcsv.AcceptChanges();
            InsertCSVRecords(tblcsv);
        }

        public void InsertCSVRecords(DataTable csvdt)
        {
            connection();

            //SqlBulkCopy objbulk = new SqlBulkCopy(con);
            var objbulk = new BulkOperation(con);
            objbulk.AllowUpdatePrimaryKeys = true;
            objbulk.DestinationTableName = "SITE_INFO";


            objbulk.ColumnMappings.Add("SERVER_ID", "SERVER_ID", true);
            objbulk.ColumnMappings.Add("SITE_NAME", "SITE_NAME");
            objbulk.ColumnMappings.Add("SITE_ADDRESS", "SITE_ADDRESS");
            objbulk.ColumnMappings.Add("SITE_CITY", "SITE_CITY");
            objbulk.ColumnMappings.Add("SITE_STATE", "SITE_STATE");
            objbulk.ColumnMappings.Add("SITE_ZIPCODE", "SITE_ZIPCODE");
            objbulk.ColumnMappings.Add("SITE_COUNTY", "SITE_COUNTY");
            objbulk.ColumnMappings.Add("SITE_INTERNALZIP", "SITE_INTERNALZIP");
            objbulk.ColumnMappings.Add("SITE_PHONE_NUM", "SITE_PHONE_NUM");
            objbulk.ColumnMappings.Add("SITE_FAX_NUM", "SITE_FAX_NUM");
            objbulk.ColumnMappings.Add("SERVER_SUBNET_ADDR", "SERVER_SUBNET_ADDR");
            objbulk.ColumnMappings.Add("SERVER_IP_ADDR", "SERVER_IP_ADDR");
            objbulk.ColumnMappings.Add("SERVER_GATEWAY_ADDR", "SERVER_GATEWAY_ADDR");
            objbulk.ColumnMappings.Add("COSTCENTER_NUM", "COSTCENTER_NUM");
            objbulk.ColumnMappings.Add("DCMF_NAME", "DCMF_NAME");
            objbulk.ColumnMappings.Add("LU_ID", "LU_ID");
            objbulk.ColumnMappings.Add("XIDPU_ID", "XIDPU_ID");
            objbulk.ColumnMappings.Add("TRAININGSITE_IND", "TRAININGSITE_IND");
            objbulk.ColumnMappings.Add("PBA_FICS_NUM", "PBA_FICS_NUM");
            objbulk.ColumnMappings.Add("PBA_CITY_ID", "PBA_CITY_ID");
            objbulk.ColumnMappings.Add("REGION_NAME", "REGION_NAME");
            objbulk.ColumnMappings.Add("SITETYPE_ID", "SITETYPE_ID");
            objbulk.ColumnMappings.Add("PBA_OFFICE_ID", "PBA_OFFICE_ID");
            objbulk.ColumnMappings.Add("SITEORIGIN_ID", "SITEORIGIN_ID");
            objbulk.ColumnMappings.Add("REGION_ID", "REGION_ID");
            objbulk.ColumnMappings.Add("PBA_BANK_ID", "PBA_BANK_ID");
            objbulk.ColumnMappings.Add("SITE_REGION_ID", "SITE_REGION_ID");



            con.Open();
            objbulk.BulkUpdate(csvdt);

            con.Close();

        }

我的逻辑是,一旦从 CSV 文件中导入信息,它就会被移动到数据表中,如果它包含空值,那么数据表中的列就会被删除。因此,没有任何内容可以映射到 BulkUpdate 列映射,因此没有应该为该列推送到数据库的数据。

但是由于某种原因这不起作用,我不知道为什么...有更好的方法吗?

任何帮助将不胜感激,谢谢。

【问题讨论】:

  • 我会建立单独的更新语句并直接执行它们。

标签: c# mysql .net csv sqlbulkcopy


【解决方案1】:

有多种解决方案可以满足您的需要。

我看到的一个问题是,如果该人正在上传具有多行的项目并且这些行具有不同的数据(例如,一列在一行上是空白的,但在第二行上是填充的),这将导致它中断,因为列将自行删除。

SITE32,,1,2,,...
SITE32,1,2,,,...

在这种情况下,第 1、3 和 4 列(此处从零开始)将被删除,因为它们的空值会破坏您的预期目的。仅当每行包含相同列中的数据时,您编写的逻辑才有效。

我建议将数据加载到临时表中并在 SQL 中完成任务,这样您就可以更好地控制数据加载过程。如果您有权访问数据库,请编写一个存储过程,然后将数据表的每一行传递给该存储过程并从那里执行您的 upsert 操作。

SqlCommand cmd = new SqlCommand("",conn());
cmd.CommandText = "CREATE TABLE #tmp (
[SERVER_ID] NVARCHAR(MAX),
[SITE_NAME] NVARCHAR(MAX),
[SITE_ADDRESS] NVARCHAR(MAX),
[SITE_CITY] NVARCHAR(MAX),
[SITE_STATE] NVARCHAR(MAX),
[SITE_ZIPCODE] NVARCHAR(MAX),
[SITE_COUNTY] NVARCHAR(MAX),
[SITE_INTERNALZIP] NVARCHAR(MAX),
[SITE_PHONE_NUM] NVARCHAR(MAX),
[SITE_FAX_NUM] NVARCHAR(MAX),
[SERVER_SUBNET_ADDR] NVARCHAR(MAX),
[SERVER_IP_ADDR] NVARCHAR(MAX),
[SERVER_GATEWAY_ADDR] NVARCHAR(MAX),
[COSTCENTER_NUM] NVARCHAR(MAX),
[DCMF_NAME] NVARCHAR(MAX),
[LU_ID] NVARCHAR(MAX),
[XIDPU_ID] NVARCHAR(MAX),
[TRAININGSITE_IND] NVARCHAR(MAX),
[PBA_FICS_NUM] NVARCHAR(MAX),
[PBA_CITY_ID] NVARCHAR(MAX),
[REGION_NAME] NVARCHAR(MAX),
[SITETYPE_ID] NVARCHAR(MAX),
[PBA_OFFICE_ID] NVARCHAR(MAX),
[SITEORIGIN_ID] NVARCHAR(MAX),
[REGION_ID] NVARCHAR(MAX),
[PBA_BANK_ID] NVARCHAR(MAX),
[SITE_REGION_ID] NVARCHAR(MAX)
);
conn.Open();
cmd.ExecuteNonQuery();

SqlBulkCopy bc = new SqlBulkCopy();
bc.DestinationTableName = "#tmp";
bc.BulkCopyTimeout = 600;
bc.WriteToServer(dt);
bc.Close();

cmd.CommandText = "UPDATE t SET t.SERVER_ID = CASE WHEN tmp.SERVER_ID <> '' THEN tmp.SERVER_ID ELSE t.SERVER_ID END, ..... t.SITE_REGION_ID = CASE WHEN tmp.SITE_REGION_ID <> '' THEN tmp.SERVER_ID ELSE t.SERVER_ID END FROM Table t INNER JOIN #tmp AS tmp ON t.SERVER_ID = tmp.SERVER_ID";
cmd.ExecuteNonQuery();
conn.Close();

以上代码需要根据您的目的进行调整和修改。这只是一个例子。请注意,您的操作之间的连接无法关闭,否则临时表将被删除。您需要使用相同的连接执行插入和更新。

【讨论】:

  • 你说得对,我没有考虑删除其他网站的列。这肯定是个问题。理想情况下,我希望使用带有存储过程的临时表,但简单地在此客户端创建表需要整个同行评审过程,因此它可能不可行。不过谢谢。
  • @ouro,您不需要添加表——只需在代码中创建一个临时 SQL 表并使用它。我添加了一些代码,希望能帮助您找到解决方案。不过,您仍然需要做一些工作才能得到它,因为我没有为您输入整个内容。而且我以前从未真正使用过这些批量操作,但希望这会有所帮助。
【解决方案2】:

建立个人更新声明
您可以链接更新

  update [test].[dbo].[Table_1] set value1 = 'newOne' where iden = 1;
  update [test].[dbo].[Table_1] set value1 = 'newTwo' where iden = 2;

但这对 SQL 注入开放,所以你应该使用参数
我不确定您是否可以将多个基于参数的更新链接到一个
如果不是,速度是一个问题,我会做一个更新异步,这样你就可以在当前执行时创建下一个更新。

【讨论】:

    猜你喜欢
    • 2017-12-20
    • 2012-09-28
    • 2019-03-14
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 2014-03-12
    • 1970-01-01
    • 2020-08-17
    相关资源
    最近更新 更多