From bbc4f91d9b02868987588abc8a858b9523b52c47 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Fri, 13 Nov 2020 19:32:50 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E4=BC=98=E5=8C=96=20AsTreeCte=20=E5=AF=B9?= =?UTF-8?q?=20MySql=205.6=20=E7=9A=84=E5=85=BC=E5=AE=B9=EF=BC=9B#536?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 --- .../MySql/Curd/MySqlSelectTest.cs | 72 ++++++++++--------- FreeSql.Tests/FreeSql.Tests/UnitTest4.cs | 2 +- FreeSql.Tests/FreeSql.Tests/g.cs | 2 +- FreeSql/Extensions/FreeSqlGlobalExtensions.cs | 68 +++++++++++++++++- FreeSql/FreeSql.xml | 3 +- 6 files changed, 110 insertions(+), 46 deletions(-) diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 2d6d3409..b54d4d0e 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -509,14 +509,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs b/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs index 2d0bacb3..3f9792de 100644 --- a/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/MySql/Curd/MySqlSelectTest.cs @@ -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("110101", t3[0].Childs[0].Childs[1].Code); - //t3 = fsql.Select().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToTreeList(); - //Assert.Single(t3); - //Assert.Equal("100000", t3[0].Code); - //Assert.Single(t3[0].Childs); - //Assert.Equal("110000", t3[0].Childs[0].Code); - //Assert.Equal(2, t3[0].Childs[0].Childs.Count); - //Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code); - //Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code); + t3 = fsql.Select().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToTreeList(); + Assert.Single(t3); + Assert.Equal("100000", t3[0].Code); + Assert.Single(t3[0].Childs); + Assert.Equal("110000", t3[0].Childs[0].Code); + Assert.Equal(2, t3[0].Childs[0].Childs.Count); + Assert.Equal("110100", t3[0].Childs[0].Childs[0].Code); + Assert.Equal("110101", t3[0].Childs[0].Childs[1].Code); - //t3 = fsql.Select().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToList(); - //Assert.Equal(4, t3.Count); - //Assert.Equal("100000", t3[0].Code); - //Assert.Equal("110000", t3[1].Code); - //Assert.Equal("110100", t3[2].Code); - //Assert.Equal("110101", t3[3].Code); + t3 = fsql.Select().Where(a => a.Name == "中国").AsTreeCte().OrderBy(a => a.Code).ToList(); + Assert.Equal(4, t3.Count); + Assert.Equal("100000", t3[0].Code); + Assert.Equal("110000", t3[1].Code); + Assert.Equal("110100", t3[2].Code); + Assert.Equal("110101", t3[3].Code); - //t3 = fsql.Select().Where(a => a.Name == "北京").AsTreeCte().OrderBy(a => a.Code).ToList(); - //Assert.Equal(3, t3.Count); - //Assert.Equal("110000", t3[0].Code); - //Assert.Equal("110100", t3[1].Code); - //Assert.Equal("110101", t3[2].Code); + t3 = fsql.Select().Where(a => a.Name == "北京").AsTreeCte().OrderBy(a => a.Code).ToList(); + Assert.Equal(3, t3.Count); + Assert.Equal("110000", t3[0].Code); + Assert.Equal("110100", t3[1].Code); + Assert.Equal("110101", t3[2].Code); - //var select = fsql.Select() - // .Where(a => a.Name == "中国") - // .AsTreeCte() - // //.OrderBy("a.cte_level desc") //递归层级 - // ; - //// var list = select.ToList(); //自己调试看查到的数据 - //select.ToUpdate().Set(a => a.testint, 855).ExecuteAffrows(); - //Assert.Equal(855, fsql.Select() - // .Where(a => a.Name == "中国") - // .AsTreeCte().Distinct().First(a => a.testint)); + var t4 = fsql.Select().Where(a => a.Name == "东城区").AsTreeCte(up: true).ToList(); + Assert.Equal(3, t4.Count); + Assert.Equal("110101", t4[0].Code); + Assert.Equal("110000", t4[1].Code); + Assert.Equal("100000", t4[2].Code); - //Assert.Equal(4, select.ToDelete().ExecuteAffrows()); - //Assert.False(fsql.Select() - // .Where(a => a.Name == "中国") - // .AsTreeCte().Any()); + var select = fsql.Select() + .Where(a => a.Name == "中国") + .AsTreeCte() + //.OrderBy("a.cte_level desc") //递归层级 + ; + // var list = select.ToList(); //自己调试看查到的数据 + select.ToUpdate().Set(a => a.testint, 855).ExecuteAffrows(); + Assert.Equal(855, fsql.Select() + .Where(a => a.Name == "中国") + .AsTreeCte().Distinct().First(a => a.testint)); + + Assert.Equal(4, select.ToDelete().ExecuteAffrows()); + Assert.False(fsql.Select() + .Where(a => a.Name == "中国") + .AsTreeCte().Any()); } [Table(Name = "D_District")] diff --git a/FreeSql.Tests/FreeSql.Tests/UnitTest4.cs b/FreeSql.Tests/FreeSql.Tests/UnitTest4.cs index a6ff160e..84a3effb 100644 --- a/FreeSql.Tests/FreeSql.Tests/UnitTest4.cs +++ b/FreeSql.Tests/FreeSql.Tests/UnitTest4.cs @@ -30,7 +30,7 @@ namespace FreeSql.Tests }); Assert.Equal(id, item2.xxx); - fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Delete().Where("1=1").ExecuteAffrows(); fsql.Delete().Where("1=1").ExecuteAffrows(); var typeid = Guid.NewGuid(); fsql.Insert(new ts_iif_type { id = typeid, name = "type001" }).ExecuteAffrows(); diff --git a/FreeSql.Tests/FreeSql.Tests/g.cs b/FreeSql.Tests/FreeSql.Tests/g.cs index 43f83258..a4f0f11d 100644 --- a/FreeSql.Tests/FreeSql.Tests/g.cs +++ b/FreeSql.Tests/FreeSql.Tests/g.cs @@ -9,7 +9,7 @@ public class g { static Lazy mysqlLazy = new Lazy(() => 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;")) //.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) diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index 67dbad00..531a8f8b 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -1,6 +1,7 @@ using FreeSql; using FreeSql.DataAnnotations; using FreeSql.Internal.CommonProvider; +using FreeSql.Internal.ObjectPool; using System; using System.Collections; using System.Collections.Concurrent; @@ -378,10 +379,12 @@ public static partial class FreeSqlGlobalExtensions #endregion #region AsTreeCte(..) 递归查询 + static ConcurrentDictionary _dicMySqlVersion = new ConcurrentDictionary(); /// /// 使用递归 CTE 查询树型的所有子记录,或者所有父记录。 /// 通过测试的数据库:MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高 - /// 返回隐藏字段:.ToList(a => new { item = a, level = "a.cte_level", path = "a.cte_path" }) + /// 返回隐藏字段:.ToList(a => new { item = a, level = "a.cte_level", path = "a.cte_path" }) + /// * v2.0.0 兼容 MySql5.6 向上或向下查询,但不支持 pathSelector/pathSeparator 详细:https://github.com/dotnetcore/FreeSql/issues/536 /// /// /// @@ -409,6 +412,69 @@ public static partial class FreeSqlGlobalExtensions var cteName = "as_tree_cte"; if (select._orm.CodeFirst.IsSyncStructureToLower) cteName = cteName.ToLower(); 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 = ""; if (pathSelector != null) { diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index b25afc15..3632c4f9 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -4369,7 +4369,8 @@ 使用递归 CTE 查询树型的所有子记录,或者所有父记录。 通过测试的数据库:MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高 - 返回隐藏字段:.ToList(a => new { item = a, level = "a.cte_level", path = "a.cte_path" }) + 返回隐藏字段:.ToList(a => new { item = a, level = "a.cte_level", path = "a.cte_path" }) + * v2.0.0 兼容 MySql5.6 向上或向下查询,但不支持 pathSelector/pathSeparator 详细:https://github.com/dotnetcore/FreeSql/issues/536