- 优化 AsTreeCte 对 MySql 5.6 的兼容;#536

This commit is contained in:
2881099 2020-11-13 19:32:50 +08:00
parent cf60f26759
commit bbc4f91d9b
6 changed files with 110 additions and 46 deletions

View File

@ -509,14 +509,5 @@
<param name="that"></param> <param name="that"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{FreeSql.FluentDataFilter},System.Reflection.Assembly[])">
<summary>
批量注入 Repository可以参考代码自行调整
</summary>
<param name="services"></param>
<param name="globalDataFilter"></param>
<param name="assemblies"></param>
<returns></returns>
</member>
</members> </members>
</doc> </doc>

View File

@ -1989,43 +1989,49 @@ WHERE ((b.`IsFinished` OR a.`TaskType` = 3) AND b.`EnabledMark` = 1)", groupsql1
Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code); Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code);
Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code); Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code);
//t3 = fsql.Select<VM_District_Child>().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToTreeList(); t3 = fsql.Select<VM_District_Child>().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToTreeList();
//Assert.Single(t3); Assert.Single(t3);
//Assert.Equal("100000", t3[0].Code); Assert.Equal("100000", t3[0].Code);
//Assert.Single(t3[0].Childs); Assert.Single(t3[0].Childs);
//Assert.Equal("110000", t3[0].Childs[0].Code); Assert.Equal("110000", t3[0].Childs[0].Code);
//Assert.Equal(2, t3[0].Childs[0].Childs.Count); Assert.Equal(2, t3[0].Childs[0].Childs.Count);
//Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code); Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code);
//Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code); Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code);
//t3 = fsql.Select<VM_District_Child>().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToList(); t3 = fsql.Select<VM_District_Child>().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToList();
//Assert.Equal(4, t3.Count); Assert.Equal(4, t3.Count);
//Assert.Equal("100000", t3[0].Code); Assert.Equal("100000", t3[0].Code);
//Assert.Equal("110000", t3[1].Code); Assert.Equal("110000", t3[1].Code);
//Assert.Equal("110100", t3[2].Code); Assert.Equal("110100", t3[2].Code);
//Assert.Equal("110101", t3[3].Code); Assert.Equal("110101", t3[3].Code);
//t3 = fsql.Select<VM_District_Child>().Where(a => a.Name == "北京").AsTreeCte().OrderBy(a => a.Code).ToList(); t3 = fsql.Select<VM_District_Child>().Where(a => a.Name == "北京").AsTreeCte().OrderBy(a => a.Code).ToList();
//Assert.Equal(3, t3.Count); Assert.Equal(3, t3.Count);
//Assert.Equal("110000", t3[0].Code); Assert.Equal("110000", t3[0].Code);
//Assert.Equal("110100", t3[1].Code); Assert.Equal("110100", t3[1].Code);
//Assert.Equal("110101", t3[2].Code); Assert.Equal("110101", t3[2].Code);
//var select = fsql.Select<VM_District_Child>() var t4 = fsql.Select<VM_District_Child>().Where(a => a.Name == "东城区").AsTreeCte(up: true).ToList();
// .Where(a => a.Name == "中国") Assert.Equal(3, t4.Count);
// .AsTreeCte() Assert.Equal("110101", t4[0].Code);
// //.OrderBy("a.cte_level desc") //递归层级 Assert.Equal("110000", t4[1].Code);
// ; Assert.Equal("100000", t4[2].Code);
//// var list = select.ToList(); //自己调试看查到的数据
//select.ToUpdate().Set(a => a.testint, 855).ExecuteAffrows();
//Assert.Equal(855, fsql.Select<VM_District_Child>()
// .Where(a => a.Name == "中国")
// .AsTreeCte().Distinct().First(a => a.testint));
//Assert.Equal(4, select.ToDelete().ExecuteAffrows()); var select = fsql.Select<VM_District_Child>()
//Assert.False(fsql.Select<VM_District_Child>() .Where(a => a.Name == "中国")
// .Where(a => a.Name == "中国") .AsTreeCte()
// .AsTreeCte().Any()); //.OrderBy("a.cte_level desc") //递归层级
;
// var list = select.ToList(); //自己调试看查到的数据
select.ToUpdate().Set(a => a.testint, 855).ExecuteAffrows();
Assert.Equal(855, fsql.Select<VM_District_Child>()
.Where(a => a.Name == "中国")
.AsTreeCte().Distinct().First(a => a.testint));
Assert.Equal(4, select.ToDelete().ExecuteAffrows());
Assert.False(fsql.Select<VM_District_Child>()
.Where(a => a.Name == "中国")
.AsTreeCte().Any());
} }
[Table(Name = "D_District")] [Table(Name = "D_District")]

View File

@ -30,7 +30,7 @@ namespace FreeSql.Tests
}); });
Assert.Equal(id, item2.xxx); Assert.Equal(id, item2.xxx);
fsql.Delete<ts_iif>().Where("1=1").ExecuteAffrows(); fsql.Delete<ts_iif_topic>().Where("1=1").ExecuteAffrows();
fsql.Delete<ts_iif_type>().Where("1=1").ExecuteAffrows(); fsql.Delete<ts_iif_type>().Where("1=1").ExecuteAffrows();
var typeid = Guid.NewGuid(); var typeid = Guid.NewGuid();
fsql.Insert(new ts_iif_type { id = typeid, name = "type001" }).ExecuteAffrows(); fsql.Insert(new ts_iif_type { id = typeid, name = "type001" }).ExecuteAffrows();

View File

@ -9,7 +9,7 @@ public class g
{ {
static Lazy<IFreeSql> mysqlLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder() static Lazy<IFreeSql> mysqlLazy = new Lazy<IFreeSql>(() => new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=5") .UseConnectionString(FreeSql.DataType.MySql, "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=5;Allow User Variables=True")
//.UseConnectionFactory(FreeSql.DataType.MySql, () => new MySql.Data.MySqlClient.MySqlConnection("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;")) //.UseConnectionFactory(FreeSql.DataType.MySql, () => new MySql.Data.MySqlClient.MySqlConnection("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;"))
//.UseConnectionString(FreeSql.DataType.MySql, "Data Source=192.168.164.10;Port=33061;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;Max pool size=10") //.UseConnectionString(FreeSql.DataType.MySql, "Data Source=192.168.164.10;Port=33061;User ID=root;Password=root;Initial Catalog=cccddd_mysqlconnector;Charset=utf8;SslMode=none;Max pool size=10")
.UseAutoSyncStructure(true) .UseAutoSyncStructure(true)

View File

@ -1,6 +1,7 @@
using FreeSql; using FreeSql;
using FreeSql.DataAnnotations; using FreeSql.DataAnnotations;
using FreeSql.Internal.CommonProvider; using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.ObjectPool;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -378,10 +379,12 @@ public static partial class FreeSqlGlobalExtensions
#endregion #endregion
#region AsTreeCte(..) #region AsTreeCte(..)
static ConcurrentDictionary<string, string> _dicMySqlVersion = new ConcurrentDictionary<string, string>();
/// <summary> /// <summary>
/// 使用递归 CTE 查询树型的所有子记录,或者所有父记录。<para></para> /// 使用递归 CTE 查询树型的所有子记录,或者所有父记录。<para></para>
/// 通过测试的数据库MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高<para></para> /// 通过测试的数据库MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高<para></para>
/// 返回隐藏字段:.ToList(a =&gt; new { item = a, level = "a.cte_level", path = "a.cte_path" }) /// 返回隐藏字段:.ToList(a =&gt; new { item = a, level = "a.cte_level", path = "a.cte_path" })<para></para>
/// * v2.0.0 兼容 MySql5.6 向上或向下查询,但不支持 pathSelector/pathSeparator 详细https://github.com/dotnetcore/FreeSql/issues/536
/// </summary> /// </summary>
/// <typeparam name="T1"></typeparam> /// <typeparam name="T1"></typeparam>
/// <param name="that"></param> /// <param name="that"></param>
@ -409,6 +412,69 @@ public static partial class FreeSqlGlobalExtensions
var cteName = "as_tree_cte"; var cteName = "as_tree_cte";
if (select._orm.CodeFirst.IsSyncStructureToLower) cteName = cteName.ToLower(); if (select._orm.CodeFirst.IsSyncStructureToLower) cteName = cteName.ToLower();
if (select._orm.CodeFirst.IsSyncStructureToUpper) cteName = cteName.ToUpper(); if (select._orm.CodeFirst.IsSyncStructureToUpper) cteName = cteName.ToUpper();
switch (select._orm.Ado.DataType) //MySql5.6
{
case DataType.MySql:
case DataType.OdbcMySql:
var mysqlConnectionString = select._orm.Ado?.ConnectionString ?? select._connection?.ConnectionString ?? "";
if (_dicMySqlVersion.TryGetValue(mysqlConnectionString, out var mysqlVersion) == false)
{
if (select._orm.Ado?.ConnectionString != null)
{
using (var mysqlconn = select._orm.Ado.MasterPool.Get())
mysqlVersion = mysqlconn.Value.ServerVersion;
}
else if (select._connection != null)
{
var isclosed = select._connection.State != ConnectionState.Open;
if (isclosed) select._connection.Open();
mysqlVersion = select._connection.ServerVersion;
if (isclosed) select._connection.Close();
}
}
if (int.TryParse((mysqlVersion ?? "").Split('.')[0], out var mysqlVersionFirst) && mysqlVersionFirst < 8)
{
if (tbref.Columns.Count > 1) throw new ArgumentException($"{tb.Type.FullName} 是父子关系,但是 MySql 8.0 以下版本中不支持组合多主键");
var mysql56Sql = "";
if (up == false)
{
mysql56Sql = $@"SELECT cte_tbc.cte_level, {select.GetAllFieldExpressionTreeLevel2().Field}
FROM (
SELECT @cte_ids as cte_ids, (
SELECT @cte_ids := group_concat({select._commonUtils.QuoteSqlName(tbref.Columns[0].Attribute.Name)})
FROM {select._commonUtils.QuoteSqlName(tb.DbName)}
WHERE find_in_set({select._commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)}, @cte_ids)
) as cte_cids, @cte_level := @cte_idcte_levels + 1 as cte_level
FROM {select._commonUtils.QuoteSqlName(tb.DbName)}, (
SELECT @cte_ids := a.{select._commonUtils.QuoteSqlName(tbref.Columns[0].Attribute.Name)}, @cte_idcte_levels := 0
FROM {select._commonUtils.QuoteSqlName(tb.DbName)} a
WHERE 1=1{select._where}
LIMIT 1) cte_tbb
WHERE @cte_ids IS NOT NULL
) cte_tbc, {select._commonUtils.QuoteSqlName(tb.DbName)} a
WHERE find_in_set(a.{select._commonUtils.QuoteSqlName(tbref.Columns[0].Attribute.Name)}, cte_tbc.cte_ids)";
select.WithSql(mysql56Sql).OrderBy("a.cte_level DESC");
select._where.Clear();
return select;
}
mysql56Sql = $@"SELECT cte_tbc.cte_level, {select.GetAllFieldExpressionTreeLevel2().Field}
FROM (
SELECT @cte_pid as cte_id, (SELECT @cte_pid := {select._commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)} FROM {select._commonUtils.QuoteSqlName(tb.DbName)} WHERE {select._commonUtils.QuoteSqlName(tbref.Columns[0].Attribute.Name)} = cte_id) as cte_pid, @cte_level := @cte_level + 1 as cte_level
FROM {select._commonUtils.QuoteSqlName(tb.DbName)}, (
SELECT @cte_pid := a.{select._commonUtils.QuoteSqlName(tbref.Columns[0].Attribute.Name)}, @cte_level := 0
FROM {select._commonUtils.QuoteSqlName(tb.DbName)} a
WHERE 1=1{select._where}
LIMIT 1) cte_tbb
) cte_tbc
JOIN {select._commonUtils.QuoteSqlName(tb.DbName)} a ON cte_tbc.cte_id = a.{select._commonUtils.QuoteSqlName(tbref.Columns[0].Attribute.Name)}";
select.WithSql(mysql56Sql).OrderBy("a.cte_level");
select._where.Clear();
return select;
}
break;
}
var sql1ctePath = ""; var sql1ctePath = "";
if (pathSelector != null) if (pathSelector != null)
{ {

View File

@ -4369,7 +4369,8 @@
<summary> <summary>
使用递归 CTE 查询树型的所有子记录,或者所有父记录。<para></para> 使用递归 CTE 查询树型的所有子记录,或者所有父记录。<para></para>
通过测试的数据库MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高<para></para> 通过测试的数据库MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高<para></para>
返回隐藏字段:.ToList(a =&gt; new { item = a, level = "a.cte_level", path = "a.cte_path" }) 返回隐藏字段:.ToList(a =&gt; new { item = a, level = "a.cte_level", path = "a.cte_path" })<para></para>
* v2.0.0 兼容 MySql5.6 向上或向下查询,但不支持 pathSelector/pathSeparator 详细https://github.com/dotnetcore/FreeSql/issues/536
</summary> </summary>
<typeparam name="T1"></typeparam> <typeparam name="T1"></typeparam>
<param name="that"></param> <param name="that"></param>