【问题标题】:Need c# code optimized to insert CSV rows to MySql需要优化 c# 代码以将 CSV 行插入 MySql
【发布时间】:2019-09-06 15:47:03
【问题描述】:

这就是我想要完成的事情......点击一个按钮,我想要:

  1. 将活动“users”表的现有数据复制到“old_users”表以进行备份
  2. 截断现有的“new_users”表以准备接受新行
  3. 解析 CSV 文件以将数据插入“new_users”表中
  4. 截断现有的“users”表,然后将“new_users”表的数据复制到“users”表中

我不是日常程序员,也很长时间没有编写程序了。但是我已经将一些代码拼凑在一起以获得一些可行的东西。以下是我当前的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.VisualBasic.FileIO;
using Renci.SshNet;
using Renci.SshNet.Common;
using MySql.Data.MySqlClient;

namespace ValidationImport
{
public partial class ValiationImport : Form
{
    public ValiationImport()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }

    private void btnImportToDatabase_Click(object sender, EventArgs e)
    {
        try
        {
            using (var client = new SshClient("1.1.1.1", "[username]", "[password]")) // establishing ssh connection to server where MySql is hosted
            {
                client.Connect();
                if (client.IsConnected)
                {
                    var portForwarded = new ForwardedPortLocal("127.0.0.1", 3306, "127.0.0.1", 3306);
                    client.AddForwardedPort(portForwarded);
                    portForwarded.Start();
                    using (MySqlConnection con = new MySqlConnection("SERVER=localhost;PORT=3306;UID=[username];PASSWORD=[password];DATABASE=[dbname]")) // MySql database credentials
                    {
                        con.Open();

                        // Copying over the users table (with structure and indexes) to the old_users table. Truncating the new_users table to prepare for new data.
                        using (MySqlCommand cmd = new MySqlCommand("DROP TABLE test_old_users; CREATE TABLE test_old_users LIKE test_users; INSERT test_old_users SELECT * FROM test_users; TRUNCATE TABLE test_new_users;", con))
                        {
                            cmd.Connection = con;
                            cmd.CommandType = CommandType.Text;
                            cmd.ExecuteNonQuery();
                        }

                        string fileName = "";
                        // Select the Validation file to import
                        OpenFileDialog dlg = new OpenFileDialog();
                        if (dlg.ShowDialog() == DialogResult.OK)
                            fileName = dlg.FileName;

                        if (fileName != "")
                        {
                            using (TextFieldParser parser = new TextFieldParser(fileName))
                            {
                                parser.TextFieldType = FieldType.Delimited;
                                parser.SetDelimiters("|");
                                parser.ReadLine(); // Skip the first row of field title headers
                                while (!parser.EndOfData)
                                {
                                    //Copy each row individually over to the MySql table
                                    string[] row = parser.ReadFields();

                                    using (MySqlCommand cmd = new MySqlCommand("INSERT INTO test_new_users (`Indv Id`, `Last Name`, `First Name`, `Middle Name`, `Birth date`, `Indv Addr Line 1`, `Indv Addr Line 2`, `Indv Addr Line 3`, `Indv City`, `Indv State`, `Indv Zip`, `Indv Country`, `Local Id`, `Local Name`, `Local User Id`, `Uniserv Id`, `Uniserv Name`, `Uniserv User Id`, `Chapter Name`, `Chapter User Id`, `Employer Id`, `Employer Name`, `Work Location Id`, `Work Location Name`, `Work Location User Id`, `Group Id`, `Group Name`, `Group Type Id`, `Group Type Name`, `SEA P01`, `Home  Phone`, `Home Phone principal Ind`, `Home Phone Unlisted Ind`, `Mobile Phone`, `Mobile Phone Principal Ind`, `Mobile Phone Unlisted Ind`, `Home Email`, `Home Email Principal Ind`, `Work Email`, `Work Email Principal Ind`, `Other Email`, `Other Email Principal Ind`) VALUES (@IndvId, @LastName, @FirstName, @MiddleName, @BirthDate, @IndvAddrLine1, @IndvAddrLine2, @IndvAddrLine3, @IndvCity, @IndvState, @IndvZip, @IndvCountry, @LocalId, @LocalName, @LocalUserId, @UniservId, @UniservName, @UniservUserId, @ChapterName, @ChapterUserId, @EmployerId, @EmployerName, @WorkLocationId, @WorkLocationName, @WorkLocationUserId, @GroupId, @GroupName, @GroupTypeId, @GroupTypeName, @SEAP01, @HomePhone, @HomePhonePrincipalInd, @HomePhoneUnlistedInd, @MobilePhone, @MobilePhonePrincipalInd, @MobilePhoneUnlistedInd, @HomeEmail, @HomeEmailPrincipalInd, @WorkEmail, @WorkEmailPrincipalInd, @OtherEmail, @OtherEmailPrincipalInd);", con))
                                    {
                                        cmd.CommandType = CommandType.Text;
                                        cmd.Parameters.AddWithValue("@IndvId", row[0]);
                                        cmd.Parameters.AddWithValue("@LastName", row[1]);
                                        cmd.Parameters.AddWithValue("@FirstName", row[2]);
                                        cmd.Parameters.AddWithValue("@MiddleName", row[3]);
                                        cmd.Parameters.AddWithValue("@BirthDate", row[4]);
                                        cmd.Parameters.AddWithValue("@IndvAddrLine1", row[5]);
                                        cmd.Parameters.AddWithValue("@IndvAddrLine2", row[6]);
                                        cmd.Parameters.AddWithValue("@IndvAddrLine3", row[7]);
                                        cmd.Parameters.AddWithValue("@IndvCity", row[8]);
                                        cmd.Parameters.AddWithValue("@IndvState", row[9]);
                                        cmd.Parameters.AddWithValue("@IndvZip", row[10]);
                                        cmd.Parameters.AddWithValue("@IndvCountry", row[11]);
                                        cmd.Parameters.AddWithValue("@LocalId", row[12]);
                                        cmd.Parameters.AddWithValue("@LocalName", row[13]);
                                        cmd.Parameters.AddWithValue("@LocalUserId", row[14]);
                                        cmd.Parameters.AddWithValue("@UniservId", row[15]);
                                        cmd.Parameters.AddWithValue("@UniservName", row[16]);
                                        cmd.Parameters.AddWithValue("@UniservUserId", row[17]);
                                        cmd.Parameters.AddWithValue("@ChapterName", row[18]);
                                        cmd.Parameters.AddWithValue("@ChapterUserId", row[19]);
                                        cmd.Parameters.AddWithValue("@EmployerId", row[20]);
                                        cmd.Parameters.AddWithValue("@EmployerName", row[21]);
                                        cmd.Parameters.AddWithValue("@WorkLocationId", row[22]);
                                        cmd.Parameters.AddWithValue("@WorkLocationName", row[23]);
                                        cmd.Parameters.AddWithValue("@WorkLocationUserId", row[24]);
                                        cmd.Parameters.AddWithValue("@GroupId", row[25]);
                                        cmd.Parameters.AddWithValue("@GroupName", row[26]);
                                        cmd.Parameters.AddWithValue("@GroupTypeId", row[27]);
                                        cmd.Parameters.AddWithValue("@GroupTypeName", row[28]);
                                        cmd.Parameters.AddWithValue("@SEAP01", row[29]);
                                        cmd.Parameters.AddWithValue("@HomePhone", row[30]);
                                        cmd.Parameters.AddWithValue("@HomePhonePrincipalInd", row[31]);
                                        cmd.Parameters.AddWithValue("@HomePhoneUnlistedInd", row[32]);
                                        cmd.Parameters.AddWithValue("@MobilePhone", row[33]);
                                        cmd.Parameters.AddWithValue("@MobilePhonePrincipalInd", row[34]);
                                        cmd.Parameters.AddWithValue("@MobilePhoneUnlistedInd", row[35]);
                                        cmd.Parameters.AddWithValue("@HomeEmail", row[36]);
                                        cmd.Parameters.AddWithValue("@HomeEmailPrincipalInd", row[37]);
                                        cmd.Parameters.AddWithValue("@WorkEmail", row[38]);
                                        cmd.Parameters.AddWithValue("@WorkEmailPrincipalInd", row[39]);
                                        cmd.Parameters.AddWithValue("@OtherEmail", row[40]);
                                        cmd.Parameters.AddWithValue("@OtherEmailPrincipalInd", row[41]);
                                        cmd.ExecuteNonQuery();
                                    }
                                }
                            }
                        }

                        // Copying over the new_users table (with structure and indexes) to the users table.
                        using (MySqlCommand cmd = new MySqlCommand("DROP TABLE test_users; CREATE TABLE test_users LIKE test_new_users; INSERT test_users SELECT * FROM test_new_users;", con))
                        {
                            cmd.Connection = con;
                            cmd.CommandType = CommandType.Text;
                            cmd.ExecuteNonQuery();
                        }
                        con.Close();
                    }
                    client.Disconnect();
                }
                else
                {
                    Console.WriteLine("Client cannot be reached...");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
}

这一切都在“工作”......除了我在 Visual Studio 中处于调试模式时出现错误。关于处理时间太长的事情。哦,你知道吗,CSV 文件中有大约 38,000 行需要插入到表中。

如果您有任何建议或新代码供我试用,我将不胜感激!谢谢!

【问题讨论】:

  • 对于导入部分,您应该能够创建一次插入的命令对象,一次添加它的参数,并在循环之前准备它;然后,只需设置参数值并为每次迭代执行。至于第一个“备份”部分;你确定它有效吗?上次我使用 MySqlCommands 时,它们的默认模式不允许在一次执行中进行多个查询。
  • 如果慢在解析部分,可以使用ExcelDataReader解析文件,速度很快。如果这是您的问题,我可以分享一个示例代码。
  • 速度慢在 INSERTing 部分。在弹出关于它需要很长时间的错误之前,它通过了大约 2,000 行。我一次只解析 1 行并将该行插入到 MySql 表中。我愿意接受有关如何做得更好的建议。 Uueerdo,你能详细介绍一下你评论的前半部分吗?是的,我确信备份部分在单个执行命令中使用多个查询正常工作。

标签: c# mysql visual-studio sql-insert


【解决方案1】:

在许多情况下,您最终会循环遍历对不同数据重复运行相同查询的数据,您可以通过在循环开始之前准备一次查询来帮助加快处理速度。

using (var cn = new MySqlConnection(...))
using (var cmd = cn.CreateCommand())
{
   // Setup command
   cmd.CommandText = "Some query";
   var param1 = cmd.Parameters.Add("@paramName1", MySQLDbType.sometype);
   var param2 = cmd.Parameters.Add("@paramName2", MySQLDbType.sometype);
   cmd.Prepare();

   // get data

   // loop over data
   foreach(var d in data)
   {
      param1.Value = d.somevalue;
      param2.Value = d.anothervalue;
      cmd.ExecuteNonQuery();
   }
}

您还可以通过索引来寻址参数,例如cmd.Parameters[0].Value。

以这种方式准备语句可以减少每次迭代解析查询本身的工作开销。


在我必须处理的一种情况下,我发现最好的解决方案是将上述内容与使用多个值列表的 INSERT 查询结合起来,形式为 INSERT INTO X(a,b,c) VALUES (?,?,?),(?,?,?),(?,?,?)。它使实际实现变得更加复杂(必须适当地处理最后不会填充所有值列表的“剩余物”),并且依赖于通过索引来处理参数;但确实有所作为。


编辑:处理关闭你的 ssh 连接的伪代码

connect = true
Open datasource
while (data remains)
{
   if (connect)
   {
      init ssh client
      init mysql connection
      init mysql command
      connect = false
   }
   while (data remains and enough time remaining)
   {
      get next data
      set command parameters
      execute command
   }
   if (not enough time remaining)
   {
       close current connection and client
       connect = true
   }
}

【讨论】:

  • 所以在你的“//获取数据”部分。我应该解析 CSV 文件并将其转储到数据表或其他东西中?
  • 基本上是的。在您的问题的上下文中,这可能是解析器初始化,您可以用您的 while 替换 foreach 并使用 ReadFields 调用开始每次迭代。然后只需执行param1.Value = row[0]; 之类的操作。我用作示例的收集循环逻辑没有什么特别之处,它只是为了简洁起见。导入部分设置命令,定义其参数,并在循环之前准备它......然后在循环中分配(而不是添加)参数值。
  • 我按照你上面的建议做了所有的事情,但我仍然遇到同样的错误。这是我得到的确切错误:发生了 ContextSwitchDeadlock 消息:托管调试助手 'ContextSwitchDeadlock' 在 [exe 文件位置] 中检测到问题
  • 显然,调用Application.DoEvents(); 可以解决问题。解析并将 38,000 行插入到远程 MySql 表中大约需要 15 分钟,但无论如何......它可以工作,哈哈。感谢您的帮助!
  • 呃......我认为 ssh 隧道在 10 分钟左右超时。它通过了大约 1/2 的文件。我想我将不得不先解析数据并将其保存到数据表中。然后可能创建一个巨大的插入语句,以便 ssh 隧道只打开几秒钟?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-27
  • 1970-01-01
  • 1970-01-01
  • 2011-11-06
  • 1970-01-01
  • 2013-05-22
相关资源
最近更新 更多