如何使用 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。不要使用字符串来表示Date
s 或DateTime
s 或Time
s、数字或任何其他非本机字符串。
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# 中的字符串?