.net core 导入大批量数据到数据库中的一种方式
因为addrange()+savechanges()的插入数据的效率对大批量数据而言十分低,所以在寻求一种可以更快速将数据插入到数据库中的方式。
在网上查了一下,发现有人说通过生成insert的sql语句执行的效率很高,所以写了一个通用的方法,此方法要求模型中需要导入的字段必须有column属性,因为我们用的是mysql,命名要求词间用“_”分隔,所以模型中的需要映射到数据库的字段上基本都有column属性,以此也排除了导航属性和[NotMapped]的字段。
14个字段的表插入速度平均大概为1min20s 5W条数据。
适用场景:单表大批量数据导入,不需要使用事务(需要事务的话,改造一下应该也可以)
需要优化但我不会优化的地方:生成sql的时候会花费50s左右,我觉得对于5W条数据来说太长了,我本来以为最多超不过10s,没想到竟然花费了这么久,不知哪位可以帮忙给看看哪里有优化的空间。
直接上代码了
#region 导入大批量数据到数据库中 /// <summary> /// 导入大批量数据到数据库中 (模型中需要进行导入的字段必须有column属性) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="datas">数据</param> /// <param name="length">每次导入的数量</param> /// <returns></returns> public async Task<int> ImportBigDatas<T>(List<T> datas, int length = 50000) where T : class { var tableName = dbStoreReadWrite.GetTableName(typeof(T)).TableName; if (tableName.IsNullOrEmpty()) { throw new ArgumentNullException($"“{nameof(T)}”未进行模型配置"); } var sum = 0; for (int i = 0; i <= datas.Count / length; i++) { var sql = HistoryExtension.GenerateInsertStr(tableName, datas.Skip(i * length).Take(length).ToList()); sum += await dbStoreReadWrite.Database.ExecuteSqlRawAsync(sql); } return sum; } #endregion
/// <summary> /// 生成插入数据库的sql语句 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tableName"></param> /// <param name="sources"></param> /// <returns></returns> public static string GenerateInsertStr<T>(string tableName, List<T> sources) where T : class { var properties = typeof(T).GetProperties().ToList(); var sql = new StringBuilder($"insert into {tableName}("); foreach (var e in properties) { if (e.CustomAttributes.Count() == 0 || e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault() == null || e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault().ConstructorArguments.Count == 0 || e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault().ConstructorArguments.FirstOrDefault() == null) { continue; } sql.Append(e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault().ConstructorArguments.FirstOrDefault().Value.ToString()); if (e == properties[^1]) { sql.Append(")"); } else { sql.Append(","); } } sql.Append(" values"); sources.ForEach(a => { sql.Append('('); foreach (var e in properties) { object value = null; if (e.CustomAttributes.Count() == 0 || e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault() == null || e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault().ConstructorArguments.Count == 0 || e.CustomAttributes.Where(e => e.AttributeType == typeof(ColumnAttribute)).FirstOrDefault().ConstructorArguments.FirstOrDefault() == null) { continue; } value = e.GetValue(a); if (e.PropertyType.IsEnum) { if (value != null) { value = ((int)e.GetValue(a)).ToString(); } } else if (e.PropertyType.Name.Contains("Boolean")) { if (value.ToString() == "True") { value = "1"; } else if (value.ToString() == "False") { value = "0"; } } if (value == null) { if (e == properties[^1]) { sql.Append("null"); } else { sql.Append("null,"); } continue; } // 如果有最大长度的限制,则需要对字符串进行截取。 int? maxLength = null; var maxLengthAttr = e.CustomAttributes.Where(a => IsHaveMaxLength(a.AttributeType)).FirstOrDefault(); if (maxLengthAttr != null && maxLengthAttr.ConstructorArguments.Where(e => e.ArgumentType == typeof(int)).Select(e => e.Value).FirstOrDefault() != null) { maxLength = (int)maxLengthAttr.ConstructorArguments.Where(e => e.ArgumentType == typeof(int)).Select(e => e.Value).FirstOrDefault(); } sql.Append("'"); sql.Append((maxLength.HasValue && value?.ToString().Replace("'", "").Replace("\\", "").Trim().Length > maxLength.Value) ? value?.ToString().Replace("'", "").Replace("\\", "").Trim()[..maxLength.Value] : value?.ToString().Replace("'", "").Replace("\\", "").Trim()); if (e == properties[^1]) { sql.Append("'"); } else { sql.Append("',"); } } if (a != sources[^1]) { sql.Append("),"); } else { sql.Append(')'); } }); return sql.ToString(); }
/// <summary> /// 判断是否存在maxlength特性 /// </summary> /// <param name="type"></param> /// <returns></returns> private static bool IsHaveMaxLength(Type type) { while (type.BaseType != typeof(object)) { if (type == typeof(MaxLengthAttribute)) { return true; } return IsHaveMaxLength(type.BaseType); } return false; }
需要优化但我不会优化的地方:生成sql的时候会花费50s左右,我觉得对于5W条数据来说太长了,我本来以为最多超不过10s,没想到竟然花费了这么久,不知哪位可以帮忙给看看哪里有优化的空间。
浙公网安备 33010602011771号