如何使用 C# 在列有日期的情况下将多行插入 MySql?

Posted

技术标签:

【中文标题】如何使用 C# 在列有日期的情况下将多行插入 MySql?【英文标题】:How do I insert multiple rows into MySql using C# where columns have dates? 【发布时间】:2019-04-10 18:58:59 【问题描述】:

以下类从 API json 响应中捕获 SendQueue 对象:

public class SendQueue

    public DateTime SendDate  get; set; 
    public int StatusCode  get; set; 
    public string StatusMessage  get; set; 


当我收到对象时,我将 SendDate 值转换为字符串以插入 mysql 数据库,但格式显然不被 DB 接受,因此插入失败。

当我将值转换为字符串时,SendDate 的格式是 "dd/MM/yyyy hh:mm:ss" 而数据库接受 "yyyy-MM-dd hh :mm:ss"

当然,更好的处理方式是通过参数化查询。但由于我使用的是批量插入,我无法使用 DateTime 对象。

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);

如果我use Bulk Insert 也会遇到同样的问题,因为日期最终会转换为存储在文件中的字符串。

【问题讨论】:

为什么将日期作为字符串存储在数据库中? 3.也不是,将 DateTime 值格式化为字符串的正确位置是在您需要显示它时。数据库应该能够接受 DateTime ,而不是格式化为字符串。 您可以在请求到达 WEB API 和存储到 DB 之前解析 Datetime。这将为您提供更多控制,因为您可以根据您将存储它的数据库引擎对其进行格式化。如果您知道数据库接受的存储格式是什么,您可以在将其发送到移动或 Web 应用程序之前对其进行格式化,并且您将永远不要改变它。 @JohnLBevan 这在您使用批量插入时不起作用。参数化查询最好用于一条语句。但是我准备了我的数据,以便在批处理语句中使用它们(例如INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);)我怀疑这是否适用。 @JohnLBevan 完全正确。 SQL Server 要简单得多。是的,这是我的问题。 【参考方案1】:

您应该在架构中使用适当的DateTime type。不要使用字符串来表示Dates 或DateTimes 或Times、数字或任何其他非本机字符串。

MySql 允许以下模式类型:

DATE
TIME
DATETIME
TIMESTAMP
YEAR

在这种情况下,最合适的应该是DATETIME。这与 .net 类型 System.DateTime 一致。如果您使用的是 ADO.NET,那么您应该在查询中使用参数。示例:

using(MySqlCommand m = new MySqlCommand("INSERT INTO table (sendDate) VALUES (@sendDate)"))

     m.Parameters.Add("@sendDate", MySqlDbType.DateTime).Value = myObj.SendDate;
     // rest of code

批量插入

您不必一次运行一个插入语句。您可以轻松地创建一个包含多个要插入的元组的语句。这是使用 1 列执行此操作的示例 c# 代码。您可以使用每个元组的多个值轻松扩展它。

var sql = "INSERT INTO table (sendDate) VALUES (";
var parameters = input.Select((queue, i) => new MySqlParameter("@sendDate" + i.ToString(), MySqlDbType.DateTime)).ToArray();
sql += string.Join("), (", parameters.Select(_ => _.ParameterName)) + ")";
for (int i = 0; i < parameters.Length; i++)

    parameters[i].Value = input[i].SendDate;


using(var connection = new MySqlConnection(connectionString))
using(var command = new MySqlCommand(sql, connection))

  command.Parameters.AddRange(parameters);
  connection.Open();
  command.ExecuteScalar();


何时转换?

代码堆栈应始终在调用堆栈中尽可能晚地将DateTime 转换为string,通常在表示层中(人类必须阅读它)。反之亦然,string 输入应尽早转换为DateTime,沿着调用堆栈向下(同样,在表示层)。


为什么不使用 varchar 表示日期?

如果您想知道为什么您不应该将DateTime 存储为string,请参阅之前的答案:When to use VARCHAR and DATE/DATETIME。我会引用重要的东西:

VARCHAR 版本的一些缺点:

您无法轻松地在 VARCHAR 版本中添加/减去天数。 很难仅提取月/年。 没有什么能阻止您将非日期数据放入数据库的 VARCHAR 列中。 VARCHAR 版本是特定于文化的。 您不能轻松地对日期进行排序。 如果您想稍后更改格式,则很难。 这是非常规的,这将使其他开发人员更难理解。 在许多环境中,使用 VARCHAR 将使用更多存储空间。这对于少量数据可能无关紧要,但在具有数百万行数据的商业环境中,这可能会产生很大的不同。

时区和价值观

如果适用于您的应用程序,请不要忘记时区。建议始终在数据库中存储 DateTime UTC 值,而不是本地时区值。然后可以将 UTC 值发送到表示层,其中表示层负责将本地时区应用于该值(可选)。一些 UI 框架在 DateTime 控件中内置了执行此操作的机制。同样,反之亦然,让表示层将任何输入转换为 UTC DateTime 值并将其发送回调用堆栈到服务器。

【讨论】:

我没有使用参数化查询的原因是我用一个脚本插入了许多记录,我没有找到一个好的库来在 MySQL 中进行批量插入。 当我们有几十万行时,使用您如此出色描述的类型会减慢单词的速度。我要么需要使用批量插入,这要求首先将整个数据集写入 .csv 文件,但后来我意识到一个包含许多行的插入比单次插入要快得多,这就是我采用这种方法的原因。跨度> @Disasterkid - 见补充,标题Batch Insert。您可以创建在同一命令中插入数据的批处理。示例代码可以根据目标的宽度使用附加值进行扩展。【参考方案2】:

根据您更新的问题,似乎根本问题是您需要一种通过单个插入语句插入多行的方法。

理想情况下,您应该提供一个可以接受表类型作为输入的存储过程,就像在 SqlServer 世界中所做的那样:Insert entire DataTable into database at once instead of row by row?

鉴于在撰写本文时这不是 MySQL 的一个选项,但是有一些替代方案...

如果您需要这样做是因为您需要在同一个事务中插入所有行,只需将您的插入内容包装在一个事务中即可。代码会是这样的(未经测试,因为我没有 MySQL)。

public void InsertMySql(string connectionString, string command, DataTable data)

    using (var connection = new MySqlConnection(connectionString))
    
        connection.Open();

        using (var transaction = connection.BeginTransaction())
        
            try
            
                using (MySqlCommand cmd = new MySqlCommand(command, connection))
                
                    cmd.CommandType = CommandType.Text;
                    foreach (var row in data.Rows)
                    
                        cmd.Parameters.Clear();
                        foreach (var cell in data.Columns)
                        
                            cmd.Parameters.AddWithValue($"@cell.Name", row[cell]);
                        
                        cmd.ExecuteNonQuery();
                    
                
                transaction.Commit();
            
            catch 
            
                transaction.Rollback();
                throw;
            
        
    

如果您出于性能原因需要这样做,另一种选择可能是查看第三方库,例如:https://github.com/zzzprojects/Bulk-Operations。我没有亲自尝试过,但该库看起来很有前途/发布者(zzzprojects)非常知名和受人尊敬/在其他项目中维护流行的站点 SQL Fiddle。

public static void InsertMySql(string connectionString, DataTable data)

    using (var connection = new MySqlConnection(connectionString))
    
        connection.Open();
        var bulk = new BulkOperation(connection);
        bulk.BulkInsert(data); //assumes your data table's table name, column names/definitions match your table's; I think.  See https://bulk-operations.net/bulk-insert for what documentation's available
    

【讨论】:

我可以考虑交易。但是,在一个事务中使用多个单个参数化 INSERT 是否比在没有该事务的情况下运行相同数量的单个参数化 INSERT 更快? / 以这种方式使用事务是否比批量插入更快? 我没有建议使用事务方法来提高性能,而是考虑到功能影响(即,如果您插入 3 行,那么其他查看数据库的人在事务提交之前将看不到任何行,那么他们将见 3;而如果没有事务,如果一行一行插入,他们可能会看到 1 或 2 行;在某些情况下这可能是无效的(例如,您为贷方和借方创建不同行的财务系统,并要求它们总是要平衡)。 事务可以提高性能,因为可以一次对所有行执行一些检查,而不是针对每个单独的插入;但它们会通过占用资源/导致阻塞来影响性能。如果您的目标是性能,请尝试使用真实数据和负载试验这两种方法,以了解真实的输出。

以上是关于如何使用 C# 在列有日期的情况下将多行插入 MySql?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用数据框的情况下将一行分解为多行?

如何在不使用 ToString() 的情况下将 Int 转换为 C# 中的字符串?

SSIS 在不使用 SQL 的情况下将多行合并并连接成单行

如何在不复制 C# 的情况下将结构数组元素提取到变量中?

如何在不使用 C# 中的 T 对象的情况下将 Json 数组转换为单个 JSON 对象?

Excel 如何批量插入多行空白行