Skip to content

Commit 09f3f1a

Browse files
committed
- 增加 IInsertOrUpdate 高性能方法 ExecuteOracleBulkCopy/ExecuteDmBulkCopy;
1 parent 0760219 commit 09f3f1a

File tree

7 files changed

+112
-19
lines changed

7 files changed

+112
-19
lines changed

Examples/base_entity/Entities/User.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class User1 : BaseEntity<User1, Guid>
5555
/// <summary>
5656
/// 描述
5757
/// </summary>
58-
[MaxLength(4000)]
58+
[MaxLength(2000)]
5959
public string Description { get; set; }
6060
}
6161

Examples/base_entity/Program.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,8 +552,8 @@ static void Main(string[] args)
552552
//.UseConnectionString(FreeSql.DataType.Oracle, "user id=user1;password=123456;data source=//127.0.0.1:1521/XE;Pooling=true;Max Pool Size=2")
553553
//.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper)
554554

555-
//.UseConnectionString(FreeSql.DataType.Dameng, "server=127.0.0.1;port=5236;user id=2user;password=123456789;database=2user;poolsize=5;min pool size=1")
556-
//.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper)
555+
.UseConnectionString(FreeSql.DataType.Dameng, "server=127.0.0.1;port=5236;user id=2user;password=123456789;database=2user;poolsize=5;min pool size=1")
556+
.UseNameConvert(FreeSql.Internal.NameConvertType.ToUpper)
557557

558558
//.UseConnectionString(FreeSql.DataType.OdbcMySql, "Driver={MySQL ODBC 8.0 Unicode Driver};Server=127.0.0.1;Persist Security Info=False;Trusted_Connection=Yes;UID=root;PWD=root;DATABASE=cccddd_odbc;Charset=utf8;SslMode=none;Max pool size=2")
559559

@@ -578,9 +578,15 @@ static void Main(string[] args)
578578
BaseEntity.Initialization(fsql, () => _asyncUow.Value);
579579
#endregion
580580

581+
fsql.CodeFirst.GetTableByEntity(typeof(User1)).Columns.Values.ToList().ForEach(col =>
582+
{
583+
col.Comment = "";
584+
});
585+
fsql.Insert(Enumerable.Range(0, 100).Select(a => new User1 { Id = Guid.NewGuid(), Nickname = $"nickname{a}", Username = $"username{a}", Description = $"desc{a}" }).ToArray()).ExecuteAffrows();
586+
581587
fsql.InsertOrUpdate<User1>()
582588
.SetSource(fsql.Select<User1>().ToList())
583-
.ExecuteSqlBulkCopy();
589+
.ExecuteDmBulkCopy();
584590

585591
var updatejoin01 = fsql.Update<User1>()
586592
.Join(fsql.Select<UserGroup>(), (a, b) => a.GroupId == b.Id)

FreeSql/Internal/Model/ColumnInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ColumnInfo
1313
public string CsName { get; set; }
1414
public Type CsType { get; set; }
1515
public ColumnAttribute Attribute { get; set; }
16-
public string Comment { get; internal set; }
16+
public string Comment { get; set; }
1717
public string DbTypeText { get; internal set; }
1818
public string DbDefaultValue { get; internal set; }
1919
public string DbInsertValue { get; internal set; }

FreeSql/Internal/Model/TableInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public class TableInfo
2424
public string DbName { get; set; }
2525
public string DbOldName { get; set; }
2626
public bool DisableSyncStructure { get; set; }
27-
public string Comment { get; internal set; }
27+
public string Comment { get; set; }
2828
public bool IsRereadSql { get; internal set; }
2929
public bool IsDictionaryType { get; internal set; }
3030

Providers/FreeSql.Provider.Dameng/DamengExtensions.cs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,57 @@ public static partial class FreeSqlDamengGlobalExtensions
2020
static FreeSql.Dameng.DamengAdo _damengAdo = new FreeSql.Dameng.DamengAdo();
2121

2222
#region ExecuteDmBulkCopy
23-
23+
/// <summary>
24+
/// 批量插入或更新(操作的字段数量超过 2000 时收益大)<para></para>
25+
/// 实现原理:使用 DmBulkCopy 插入临时表,再执行 MERGE INTO t1 using (select * from #temp) ...
26+
/// </summary>
27+
/// <typeparam name="T"></typeparam>
28+
/// <param name="that"></param>
29+
/// <returns></returns>
30+
public static int ExecuteDmBulkCopy<T>(this IInsertOrUpdate<T> that) where T : class
31+
{
32+
var upsert = that as InsertOrUpdateProvider<T>;
33+
if (upsert._source.Any() != true || upsert._tempPrimarys.Any() == false) return 0;
34+
var state = ExecuteDmBulkCopyState(upsert);
35+
return UpdateProvider.ExecuteBulkUpsert(upsert, state, insert => insert.ExecuteDmBulkCopy());
36+
}
37+
static NativeTuple<string, string, string, string, string[]> ExecuteDmBulkCopyState<T>(InsertOrUpdateProvider<T> upsert) where T : class
38+
{
39+
if (upsert._source.Any() != true) return null;
40+
var _table = upsert._table;
41+
var _commonUtils = upsert._commonUtils;
42+
var updateTableName = upsert._tableRule?.Invoke(_table.DbName) ?? _table.DbName;
43+
var tempTableName = $"Temp_{Guid.NewGuid().ToString("N").ToUpper().Substring(0, 24)}";
44+
if (upsert._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
45+
if (upsert._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
46+
if (upsert._connection == null && upsert._orm.Ado.TransactionCurrentThread != null)
47+
upsert.WithTransaction(upsert._orm.Ado.TransactionCurrentThread);
48+
var sb = new StringBuilder().Append("CREATE GLOBAL TEMPORARY TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( ");
49+
foreach (var col in _table.Columns.Values)
50+
{
51+
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" ").Append(col.Attribute.DbType.Replace("NOT NULL", ""));
52+
sb.Append(",");
53+
}
54+
var sql1 = sb.Remove(sb.Length - 1, 1).Append("\r\n) ON COMMIT PRESERVE ROWS").ToString();
55+
sb.Clear();
56+
try
57+
{
58+
upsert._sourceSql = $"select * from {tempTableName}";
59+
var sql2 = upsert.ToSql();
60+
var sql3 = $"BEGIN \r\n" +
61+
$"execute immediate 'TRUNCATE TABLE {_commonUtils.QuoteSqlName(tempTableName)}';\r\n" +
62+
$"execute immediate 'DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}';\r\n" +
63+
$"END;";
64+
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, _table.Columns.Values.Select(a => a.Attribute.Name).ToArray());
65+
}
66+
finally
67+
{
68+
upsert._sourceSql = null;
69+
}
70+
}
2471
/// <summary>
2572
/// 批量更新(更新字段数量超过 2000 时收益大)<para></para>
26-
/// 实现原理:使用 OracleBulkCopy 插入临时表,再使用 MERGE INTO 联表更新
73+
/// 实现原理:使用 DmBulkCopy 插入临时表,再使用 MERGE INTO 联表更新
2774
/// </summary>
2875
/// <typeparam name="T"></typeparam>
2976
/// <param name="that"></param>

Providers/FreeSql.Provider.Oracle/OracleExtensions.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,54 @@ public static partial class FreeSqlOracleGlobalExtensions
2727
#else
2828
#region ExecuteOracleBulkCopy
2929
/// <summary>
30+
/// 批量插入或更新(操作的字段数量超过 2000 时收益大)<para></para>
31+
/// 实现原理:使用 OracleBulkCopy 插入临时表,再执行 MERGE INTO t1 using (select * from #temp) ...
32+
/// </summary>
33+
/// <typeparam name="T"></typeparam>
34+
/// <param name="that"></param>
35+
/// <returns></returns>
36+
public static int ExecuteOracleBulkCopy<T>(this IInsertOrUpdate<T> that) where T : class
37+
{
38+
var upsert = that as InsertOrUpdateProvider<T>;
39+
if (upsert._source.Any() != true || upsert._tempPrimarys.Any() == false) return 0;
40+
var state = ExecuteOracleBulkCopyState(upsert);
41+
return UpdateProvider.ExecuteBulkUpsert(upsert, state, insert => insert.ExecuteOracleBulkCopy());
42+
}
43+
static NativeTuple<string, string, string, string, string[]> ExecuteOracleBulkCopyState<T>(InsertOrUpdateProvider<T> upsert) where T : class
44+
{
45+
if (upsert._source.Any() != true) return null;
46+
var _table = upsert._table;
47+
var _commonUtils = upsert._commonUtils;
48+
var updateTableName = upsert._tableRule?.Invoke(_table.DbName) ?? _table.DbName;
49+
var tempTableName = $"Temp_{Guid.NewGuid().ToString("N").ToUpper().Substring(0, 24)}";
50+
if (upsert._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
51+
if (upsert._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
52+
if (upsert._connection == null && upsert._orm.Ado.TransactionCurrentThread != null)
53+
upsert.WithTransaction(upsert._orm.Ado.TransactionCurrentThread);
54+
var sb = new StringBuilder().Append("CREATE GLOBAL TEMPORARY TABLE ").Append(_commonUtils.QuoteSqlName(tempTableName)).Append(" ( ");
55+
foreach (var col in _table.Columns.Values)
56+
{
57+
sb.Append(" \r\n ").Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" ").Append(col.Attribute.DbType.Replace("NOT NULL", ""));
58+
sb.Append(",");
59+
}
60+
var sql1 = sb.Remove(sb.Length - 1, 1).Append("\r\n) ON COMMIT PRESERVE ROWS").ToString();
61+
sb.Clear();
62+
try
63+
{
64+
upsert._sourceSql = $"select * from {tempTableName}";
65+
var sql2 = upsert.ToSql();
66+
var sql3 = $"BEGIN \r\n" +
67+
$"execute immediate 'TRUNCATE TABLE {_commonUtils.QuoteSqlName(tempTableName)}';\r\n" +
68+
$"execute immediate 'DROP TABLE {_commonUtils.QuoteSqlName(tempTableName)}';\r\n" +
69+
$"END;";
70+
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, _table.Columns.Values.Select(a => a.Attribute.Name).ToArray());
71+
}
72+
finally
73+
{
74+
upsert._sourceSql = null;
75+
}
76+
}
77+
/// <summary>
3078
/// 批量更新(更新字段数量超过 2000 时收益大)<para></para>
3179
/// 实现原理:使用 OracleBulkCopy 插入临时表,再使用 MERGE INTO 联表更新
3280
/// </summary>

Providers/FreeSql.Provider.SqlServer/SqlServerExtensions.cs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ public static int ExecuteSqlBulkCopy<T>(this IInsertOrUpdate<T> that, SqlBulkCop
135135
if (upsert._source.Any() != true || upsert._tempPrimarys.Any() == false) return 0;
136136
var state = ExecuteSqlBulkCopyState(upsert);
137137
return UpdateProvider.ExecuteBulkUpsert(upsert, state, insert => insert.ExecuteSqlBulkCopy(copyOptions, batchSize, bulkCopyTimeout));
138-
139138
}
140139
static NativeTuple<string, string, string, string, string[]> ExecuteSqlBulkCopyState<T>(InsertOrUpdateProvider<T> upsert) where T : class
141140
{
@@ -148,20 +147,13 @@ static NativeTuple<string, string, string, string, string[]> ExecuteSqlBulkCopyS
148147
if (upsert._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
149148
if (upsert._connection == null && upsert._orm.Ado.TransactionCurrentThread != null)
150149
upsert.WithTransaction(upsert._orm.Ado.TransactionCurrentThread);
151-
var setColumns = new List<string>();
152-
var pkColumns = new List<string>();
153-
foreach (var col in _table.Columns.Values)
154-
{
155-
if (upsert._tempPrimarys.Any(a => a.CsName == col.CsName)) pkColumns.Add(col.Attribute.Name);
156-
else if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && upsert._updateIgnore.ContainsKey(col.Attribute.Name) == false) setColumns.Add(col.Attribute.Name);
157-
}
158-
var sql1 = $"SELECT {string.Join(", ", pkColumns.Select(a => _commonUtils.QuoteSqlName(a)))}, {string.Join(", ", setColumns.Select(a => _commonUtils.QuoteSqlName(a)))} INTO {tempTableName} FROM {_commonUtils.QuoteSqlName(updateTableName)} WHERE 1=2";
150+
var sql1 = $"SELECT {string.Join(", ", _table.Columns.Values.Select(a => _commonUtils.QuoteSqlName(a.Attribute.Name)))} INTO {tempTableName} FROM {_commonUtils.QuoteSqlName(updateTableName)} WHERE 1=2";
159151
try
160152
{
161153
upsert._sourceSql = $"select * from {tempTableName}";
162154
var sql2 = upsert.ToSql();
163155
var sql3 = $"DROP TABLE {tempTableName}";
164-
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, pkColumns.Concat(setColumns).ToArray());
156+
return NativeTuple.Create(sql1, sql2, sql3, tempTableName, _table.Columns.Values.Select(a => a.Attribute.Name).ToArray());
165157
}
166158
finally
167159
{
@@ -195,7 +187,7 @@ static NativeTuple<string, string, string, string, string[]> ExecuteSqlBulkCopyS
195187
if (update._orm.CodeFirst.IsSyncStructureToLower) tempTableName = tempTableName.ToLower();
196188
if (update._orm.CodeFirst.IsSyncStructureToUpper) tempTableName = tempTableName.ToUpper();
197189
if (update._connection == null && update._orm.Ado.TransactionCurrentThread != null)
198-
update.WithTransaction(update._orm.Ado.TransactionCurrentThread);
190+
update.WithTransaction(update._orm.Ado.TransactionCurrentThread);
199191
var setColumns = new List<string>();
200192
var pkColumns = new List<string>();
201193
foreach (var col in _table.Columns.Values)

0 commit comments

Comments
 (0)