mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-01 00:05:28 +08:00 
			
		
		
		
	- 优化 Limit + Sum/Avg/Max/Min 为嵌套查询;
This commit is contained in:
		| @@ -9,7 +9,7 @@ | |||||||
| 	</PropertyGroup> | 	</PropertyGroup> | ||||||
|  |  | ||||||
| 	<PropertyGroup> | 	<PropertyGroup> | ||||||
| 		<Version>3.2.651</Version> | 		<Version>3.2.660-preview22020525</Version> | ||||||
| 	</PropertyGroup> | 	</PropertyGroup> | ||||||
| 	 | 	 | ||||||
| 	<ItemGroup> | 	<ItemGroup> | ||||||
|   | |||||||
| @@ -1001,10 +1001,15 @@ limit 0,10", t1); | |||||||
|             var subquery = select.ToSql(a => new |             var subquery = select.ToSql(a => new | ||||||
|             { |             { | ||||||
|                 all = a, |                 all = a, | ||||||
|                 count = (long)select.As("b").Sum(b => b.Id) |                 count = (long)select.As("b").Sum(b => b.Id), | ||||||
|  |                 sum2 = (long)select.As("b").Limit(10).Sum(b => b.Id) | ||||||
|             }); |             }); | ||||||
|             Assert.Equal(@"SELECT a.""Id"" as1, a.""Clicks"" as2, a.""TypeGuid"" as3, a.""Title"" as4, a.""CreateTime"" as5, ifnull((SELECT sum(b.""Id"")  |             Assert.Equal(@"SELECT a.""Id"" as1, a.""Clicks"" as2, a.""TypeGuid"" as3, a.""Title"" as4, a.""CreateTime"" as5, ifnull((SELECT sum(b.""Id"")  | ||||||
|     FROM ""tb_topic22"" b), 0) as6  |     FROM ""tb_topic22"" b), 0) as6, ifnull((SELECT  sum(ftba.""Id"") FROM (  | ||||||
|  |     SELECT b.""Id""  | ||||||
|  |     FROM ""tb_topic22"" b  | ||||||
|  |     limit 0,10 | ||||||
|  | ) ftba), 0) as7  | ||||||
| FROM ""tb_topic22"" a", subquery); | FROM ""tb_topic22"" a", subquery); | ||||||
|             var subqueryList = select.ToList(a => new |             var subqueryList = select.ToList(a => new | ||||||
|             { |             { | ||||||
|   | |||||||
| @@ -1362,9 +1362,15 @@ namespace FreeSql.Internal | |||||||
|                                             var exp3Args0 = (exp3.Arguments.FirstOrDefault() as UnaryExpression)?.Operand as LambdaExpression; |                                             var exp3Args0 = (exp3.Arguments.FirstOrDefault() as UnaryExpression)?.Operand as LambdaExpression; | ||||||
|                                             if (exp3Args0.Parameters.Count == 1 && exp3Args0.Parameters[0].Type.FullName.StartsWith("FreeSql.Internal.Model.HzyTuple`")) |                                             if (exp3Args0.Parameters.Count == 1 && exp3Args0.Parameters[0].Type.FullName.StartsWith("FreeSql.Internal.Model.HzyTuple`")) | ||||||
|                                                 exp3Args0 = new ReplaceHzyTupleToMultiParam().Modify(exp3Args0, fsqltables); |                                                 exp3Args0 = new ReplaceHzyTupleToMultiParam().Modify(exp3Args0, fsqltables); | ||||||
|                                             var sqlSum = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({ExpressionLambdaToSql(exp3Args0, tscClone1)})" })?.ToString(); |                                             var sqlSumField = $"{exp3.Method.Name.ToLower()}({ExpressionLambdaToSql(exp3Args0, tscClone1)})"; | ||||||
|  |                                             var sqlSum = tscClone1.subSelect001._limit <= 0 && tscClone1.subSelect001._skip <= 0 ? | ||||||
|  |                                                 fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({ExpressionLambdaToSql(exp3Args0, tscClone1)})" })?.ToString() : | ||||||
|  |                                                 tscClone1.subSelect001.GetNestSelectSql(exp3Args0, sqlSumField, tosqlField => | ||||||
|  |                                                     fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { tosqlField })?.ToString()); | ||||||
|                                             if (string.IsNullOrEmpty(sqlSum) == false) |                                             if (string.IsNullOrEmpty(sqlSum) == false) | ||||||
|                                                 return _common.IsNull($"({sqlSum.Replace(" \r\n", " \r\n    ")})", 0); |                                                 return tscClone1.subSelect001._limit <= 0 && tscClone1.subSelect001._skip <= 0 ? | ||||||
|  |                                                     _common.IsNull($"({sqlSum.Replace(" \r\n", " \r\n    ")})", 0) : | ||||||
|  |                                                     _common.IsNull($"({sqlSum})", 0); | ||||||
|                                             break; |                                             break; | ||||||
|                                         case "ToList": |                                         case "ToList": | ||||||
|                                         case "ToOne": |                                         case "ToOne": | ||||||
|   | |||||||
| @@ -243,6 +243,113 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|             } |             } | ||||||
|             return newExp; |             return newExp; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public ReadAnonymousTypeAfInfo GetExpressionField(Expression newexp, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) | ||||||
|  |         { | ||||||
|  |             var map = new ReadAnonymousTypeInfo(); | ||||||
|  |             var field = new StringBuilder(); | ||||||
|  |             var index = fieldAlias == FieldAliasOptions.AsProperty ? CommonExpression.ReadAnonymousFieldAsCsName : 0; | ||||||
|  |  | ||||||
|  |             _commonExpression.ReadAnonymousField(_tables, field, map, ref index, newexp, this, null, _whereGlobalFilter, null, null, true); | ||||||
|  |             return new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); | ||||||
|  |         } | ||||||
|  |         public string GetNestSelectSql(Expression select, string affield, Func<string, string> ToSql) | ||||||
|  |         { | ||||||
|  |             var allMemExps = new FindAllMemberExpressionVisitor(this); | ||||||
|  |             allMemExps.Visit(select); | ||||||
|  |             var fieldAlias = new Dictionary<string, bool>(); | ||||||
|  |             var fieldReplaced = new Dictionary<string, bool>(); | ||||||
|  |             var field = new StringBuilder(); | ||||||
|  |             foreach (var memExp in allMemExps.Result) | ||||||
|  |             { | ||||||
|  |                 var gef = GetExpressionField(memExp.Item1, FieldAliasOptions.AsProperty); | ||||||
|  |                 var geffield = gef.field; | ||||||
|  |                 if (fieldReplaced.ContainsKey(geffield)) continue; | ||||||
|  |                 fieldReplaced.Add(geffield, true); | ||||||
|  |  | ||||||
|  |                 field.Append(", ").Append(gef.field); | ||||||
|  |                 if (fieldAlias.ContainsKey(memExp.Item2.Attribute.Name)) | ||||||
|  |                 { | ||||||
|  |                     field.Append(_commonUtils.FieldAsAlias($"aas{fieldAlias.Count}")); | ||||||
|  |                     affield = affield.Replace(geffield, $"ftba.aas{fieldAlias.Count}"); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     fieldAlias.Add(memExp.Item2.Attribute.Name, true); | ||||||
|  |                     affield = affield.Replace(geffield, $"ftba.{string.Join(".", geffield.Split('.').Skip(1))}"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var sql = ToSql(field.Remove(0, 2).ToString()); | ||||||
|  |             sql = $"{_select} {affield} FROM ( \r\n    {sql.Replace("\r\n", "\r\n    ")}\r\n) ftba"; | ||||||
|  |             field.Clear(); | ||||||
|  |             return sql; | ||||||
|  |         } | ||||||
|  |         public class FindAllMemberExpressionVisitor : ExpressionVisitor | ||||||
|  |         { | ||||||
|  |             public List<NativeTuple<MemberExpression, ColumnInfo>> Result { get; set; } = new List<NativeTuple<MemberExpression, ColumnInfo>>(); | ||||||
|  |             Select0Provider _select; | ||||||
|  |             public FindAllMemberExpressionVisitor(Select0Provider select) => _select = select; | ||||||
|  |  | ||||||
|  |             protected override Expression VisitMember(MemberExpression node) | ||||||
|  |             { | ||||||
|  |                 var exps = new Stack<Expression>(); | ||||||
|  |                 Expression exp = node; | ||||||
|  |                 while (exp != null) | ||||||
|  |                 { | ||||||
|  |                     switch (exp.NodeType) | ||||||
|  |                     { | ||||||
|  |                         case ExpressionType.Parameter: | ||||||
|  |                             exps.Push(exp); | ||||||
|  |                             exp = null; | ||||||
|  |                             continue; | ||||||
|  |                         case ExpressionType.MemberAccess: | ||||||
|  |                             exps.Push(exp); | ||||||
|  |                             exp = (exp as MemberExpression)?.Expression; | ||||||
|  |                             continue; | ||||||
|  |                     } | ||||||
|  |                     return base.VisitMember(node); | ||||||
|  |                 } | ||||||
|  |                 if (exps.Any() == false) return base.VisitMember(node); | ||||||
|  |                 var firstExp = exps.Pop() as ParameterExpression; | ||||||
|  |                 if (firstExp == null) return base.VisitMember(node); | ||||||
|  |                 var tb = _select._tables.Find(a => a.Parameter == firstExp)?.Table; | ||||||
|  |                 if (tb == null) return base.VisitMember(node); | ||||||
|  |  | ||||||
|  |                 while (exps.Any()) | ||||||
|  |                 { | ||||||
|  |                     var memExp = exps.Pop() as MemberExpression; | ||||||
|  |                     if (tb.ColumnsByCs.TryGetValue(memExp.Member.Name, out var trycol) && exps.Any() == false) | ||||||
|  |                     { | ||||||
|  |                         Result.Add(NativeTuple.Create(node, trycol)); | ||||||
|  |                         return node; | ||||||
|  |                     } | ||||||
|  |                     if (tb.Properties.ContainsKey(memExp.Member.Name)) | ||||||
|  |                     { | ||||||
|  |                         tb = _select._commonUtils.GetTableByEntity(memExp.Type); | ||||||
|  |                         if (tb == null) return base.VisitMember(node); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return base.VisitMember(node); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         public class ReplaceMemberExpressionVisitor : ExpressionVisitor | ||||||
|  |         { | ||||||
|  |             Expression _findExp; | ||||||
|  |             Expression _replaceExp; | ||||||
|  |             public Expression Replace(Expression exp, Expression find, Expression replace) // object repval) | ||||||
|  |             { | ||||||
|  |                 _findExp = find; | ||||||
|  |                 _replaceExp = replace; | ||||||
|  |                 //_replaceExp = Expression.Constant(repval, find.Type); | ||||||
|  |                 return this.Visit(exp); | ||||||
|  |             } | ||||||
|  |             protected override Expression VisitMember(MemberExpression node) | ||||||
|  |             { | ||||||
|  |                 if (_findExp == node) return _replaceExp; | ||||||
|  |                 return base.VisitMember(node); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public abstract partial class Select0Provider<TSelect, T1> : Select0Provider, ISelect0<TSelect, T1> where TSelect : class |     public abstract partial class Select0Provider<TSelect, T1> : Select0Provider, ISelect0<TSelect, T1> where TSelect : class | ||||||
|   | |||||||
| @@ -64,11 +64,12 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public List<TTuple> ToList<TTuple>(string field) |         public List<TTuple> ToList<TTuple>(string field) => ToListQfPrivate<TTuple>(this.ToSql(field), field); | ||||||
|  |         public List<TTuple> ToListQfPrivate<TTuple>(string sql, string field) | ||||||
|         { |         { | ||||||
|             var ret = new List<TTuple>(); |             var ret = new List<TTuple>(); | ||||||
|             if (_cancel?.Invoke() == true) return ret; |             if (_cancel?.Invoke() == true) return ret; | ||||||
|             var sql = this.ToSql(field); |             if (string.IsNullOrEmpty(sql)) return ret; | ||||||
|             var dbParms = _params.ToArray(); |             var dbParms = _params.ToArray(); | ||||||
|             var type = typeof(TTuple); |             var type = typeof(TTuple); | ||||||
|             var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); |             var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); | ||||||
| @@ -394,15 +395,6 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|             return ToListMrPrivate<TReturn>(sql, af, otherData); |             return ToListMrPrivate<TReturn>(sql, af, otherData); | ||||||
|         } |         } | ||||||
|         protected List<TReturn> ToListMapReader<TReturn>(ReadAnonymousTypeAfInfo af) => ToListMapReaderPrivate<TReturn>(af, null); |         protected List<TReturn> ToListMapReader<TReturn>(ReadAnonymousTypeAfInfo af) => ToListMapReaderPrivate<TReturn>(af, null); | ||||||
|         protected ReadAnonymousTypeAfInfo GetExpressionField(Expression newexp, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) |  | ||||||
|         { |  | ||||||
|             var map = new ReadAnonymousTypeInfo(); |  | ||||||
|             var field = new StringBuilder(); |  | ||||||
|             var index = fieldAlias == FieldAliasOptions.AsProperty ? CommonExpression.ReadAnonymousFieldAsCsName : 0; |  | ||||||
|  |  | ||||||
|             _commonExpression.ReadAnonymousField(_tables, field, map, ref index, newexp, this, null, _whereGlobalFilter, null, null, true); |  | ||||||
|             return new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); |  | ||||||
|         } |  | ||||||
|         static ConcurrentDictionary<string, GetAllFieldExpressionTreeInfo> _dicGetAllFieldExpressionTree = new ConcurrentDictionary<string, GetAllFieldExpressionTreeInfo>(); |         static ConcurrentDictionary<string, GetAllFieldExpressionTreeInfo> _dicGetAllFieldExpressionTree = new ConcurrentDictionary<string, GetAllFieldExpressionTreeInfo>(); | ||||||
|         public class GetAllFieldExpressionTreeInfo |         public class GetAllFieldExpressionTreeInfo | ||||||
|         { |         { | ||||||
| @@ -771,12 +763,41 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|  |  | ||||||
|         protected double InternalAvg(Expression exp) |         protected double InternalAvg(Expression exp) | ||||||
|         { |         { | ||||||
|             var list = this.ToList<double>($"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"); |             var field = $"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) | ||||||
|  |             { | ||||||
|  |                 var list = this.ToList<double>(field); | ||||||
|                 return list.Sum() / list.Count; |                 return list.Sum() / list.Count; | ||||||
|             } |             } | ||||||
|         protected TMember InternalMax<TMember>(Expression exp) => this.ToList<TMember>($"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}").Max(); |  | ||||||
|         protected TMember InternalMin<TMember>(Expression exp) => this.ToList<TMember>($"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}").Min(); |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|         protected decimal InternalSum(Expression exp) => this.ToList<decimal>($"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}").Sum(); |             var list2 = ToListQfPrivate<double>(sql, field); | ||||||
|  |             return list2.Sum() / list2.Count; | ||||||
|  |         } | ||||||
|  |         protected TMember InternalMax<TMember>(Expression exp) | ||||||
|  |         { | ||||||
|  |             var field = $"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) return this.ToList<TMember>(field).Max(); | ||||||
|  |  | ||||||
|  |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|  |             return ToListQfPrivate<TMember>(sql, field).Max(); | ||||||
|  |         } | ||||||
|  |         protected TMember InternalMin<TMember>(Expression exp) | ||||||
|  |         { | ||||||
|  |             var field = $"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) return this.ToList<TMember>(field).Min(); | ||||||
|  |  | ||||||
|  |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|  |             return ToListQfPrivate<TMember>(sql, field).Min(); | ||||||
|  |         } | ||||||
|  |         protected decimal InternalSum(Expression exp) | ||||||
|  |         { | ||||||
|  |             var field = $"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) return this.ToList<decimal>(field).Sum(); | ||||||
|  |  | ||||||
|  |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|  |             return ToListQfPrivate<decimal>(sql, field).Sum(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public ISelectGrouping<TKey, TValue> InternalGroupBy<TKey, TValue>(Expression columns) |         public ISelectGrouping<TKey, TValue> InternalGroupBy<TKey, TValue>(Expression columns) | ||||||
|         { |         { | ||||||
| @@ -829,71 +850,6 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|             return this.OrderBy($"{_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true, null)} DESC"); |             return this.OrderBy($"{_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true, null)} DESC"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         class FindAllMemberExpressionVisitor : ExpressionVisitor |  | ||||||
|         { |  | ||||||
|             public List<NativeTuple<MemberExpression, ColumnInfo>> Result { get; set; } = new List<NativeTuple<MemberExpression, ColumnInfo>>(); |  | ||||||
|             Select0Provider _select; |  | ||||||
|             public FindAllMemberExpressionVisitor(Select0Provider select) => _select = select; |  | ||||||
|  |  | ||||||
|             protected override Expression VisitMember(MemberExpression node) |  | ||||||
|             { |  | ||||||
|                 var exps = new Stack<Expression>(); |  | ||||||
|                 Expression exp = node; |  | ||||||
|                 while (exp != null) |  | ||||||
|                 { |  | ||||||
|                     switch (exp.NodeType) |  | ||||||
|                     { |  | ||||||
|                         case ExpressionType.Parameter: |  | ||||||
|                             exps.Push(exp); |  | ||||||
|                             exp = null; |  | ||||||
|                             continue; |  | ||||||
|                         case ExpressionType.MemberAccess: |  | ||||||
|                             exps.Push(exp); |  | ||||||
|                             exp = (exp as MemberExpression)?.Expression; |  | ||||||
|                             continue; |  | ||||||
|                     } |  | ||||||
|                     return base.VisitMember(node); |  | ||||||
|                 } |  | ||||||
|                 if (exps.Any() == false) return base.VisitMember(node); |  | ||||||
|                 var firstExp = exps.Pop() as ParameterExpression; |  | ||||||
|                 if (firstExp == null) return base.VisitMember(node); |  | ||||||
|                 var tb = _select._tables.Find(a => a.Parameter == firstExp)?.Table; |  | ||||||
|                 if (tb == null) return base.VisitMember(node); |  | ||||||
|  |  | ||||||
|                 while (exps.Any()) |  | ||||||
|                 { |  | ||||||
|                     var memExp = exps.Pop() as MemberExpression; |  | ||||||
|                     if (tb.ColumnsByCs.TryGetValue(memExp.Member.Name, out var trycol) && exps.Any() == false) |  | ||||||
|                     { |  | ||||||
|                         Result.Add(NativeTuple.Create(node, trycol)); |  | ||||||
|                         return node; |  | ||||||
|                     } |  | ||||||
|                     if (tb.Properties.ContainsKey(memExp.Member.Name)) |  | ||||||
|                     { |  | ||||||
|                         tb = _select._commonUtils.GetTableByEntity(memExp.Type); |  | ||||||
|                         if (tb == null) return base.VisitMember(node); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 return base.VisitMember(node); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         class ReplaceMemberExpressionVisitor : ExpressionVisitor |  | ||||||
|         { |  | ||||||
|             Expression _findExp; |  | ||||||
|             Expression _replaceExp; |  | ||||||
|             public Expression Replace(Expression exp, Expression find, Expression replace) // object repval) |  | ||||||
|             { |  | ||||||
|                 _findExp = find; |  | ||||||
|                 _replaceExp = replace; |  | ||||||
|                 //_replaceExp = Expression.Constant(repval, find.Type); |  | ||||||
|                 return this.Visit(exp); |  | ||||||
|             } |  | ||||||
|             protected override Expression VisitMember(MemberExpression node) |  | ||||||
|             { |  | ||||||
|                 if (_findExp == node) return _replaceExp; |  | ||||||
|                 return base.VisitMember(node); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         public List<TReturn> InternalToList<TReturn>(Expression select) |         public List<TReturn> InternalToList<TReturn>(Expression select) | ||||||
|         { |         { | ||||||
|             var map = new ReadAnonymousTypeInfo(); |             var map = new ReadAnonymousTypeInfo(); | ||||||
| @@ -1087,34 +1043,7 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|                 var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); |                 var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); | ||||||
|                 if (GetTableRuleUnions().Count <= 1) return this.ToListMapReader<TReturn>(af).FirstOrDefault(); |                 if (GetTableRuleUnions().Count <= 1) return this.ToListMapReader<TReturn>(af).FirstOrDefault(); | ||||||
|  |  | ||||||
|                 var affield = af.field; |                 var sql = GetNestSelectSql(select, af.field, ToSql); | ||||||
|                 var allMemExps = new FindAllMemberExpressionVisitor(this); |  | ||||||
|                 allMemExps.Visit(select); |  | ||||||
|                 field.Clear(); |  | ||||||
|                 var fieldAlias = new Dictionary<string, bool>(); |  | ||||||
|                 var fieldReplaced = new Dictionary<string, bool>(); |  | ||||||
|                 foreach (var memExp in allMemExps.Result) |  | ||||||
|                 { |  | ||||||
|                     var gef = GetExpressionField(memExp.Item1, FieldAliasOptions.AsProperty); |  | ||||||
|                     var geffield = gef.field; |  | ||||||
|                     if (fieldReplaced.ContainsKey(geffield)) continue; |  | ||||||
|                     fieldReplaced.Add(geffield, true); |  | ||||||
|  |  | ||||||
|                     field.Append(", ").Append(gef.field); |  | ||||||
|                     if (fieldAlias.ContainsKey(memExp.Item2.Attribute.Name)) |  | ||||||
|                     { |  | ||||||
|                         field.Append(_commonUtils.FieldAsAlias($"aas{fieldAlias.Count}")); |  | ||||||
|                         affield = affield.Replace(geffield, $"ftba.aas{fieldAlias.Count}"); |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         fieldAlias.Add(memExp.Item2.Attribute.Name, true); |  | ||||||
|                         affield = affield.Replace(geffield, $"ftba.{string.Join(".", geffield.Split('.').Skip(1))}"); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 var sql = this.ToSql(field.Remove(0, 2).ToString()); |  | ||||||
|                 sql = $"{_select} {affield} FROM ( \r\n    {sql.Replace("\r\n", "\r\n    ")}\r\n) ftba"; |  | ||||||
|                 return ToListMrPrivate<TReturn>(sql, af, null).FirstOrDefault(); |                 return ToListMrPrivate<TReturn>(sql, af, null).FirstOrDefault(); | ||||||
|             } |             } | ||||||
|             finally |             finally | ||||||
| @@ -1173,11 +1102,12 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|             return ret; |             return ret; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         async public Task<List<TTuple>> ToListAsync<TTuple>(string field, CancellationToken cancellationToken) |         public Task<List<TTuple>> ToListAsync<TTuple>(string field, CancellationToken cancellationToken) => ToListQfPrivateAsync<TTuple>(this.ToSql(field), field, cancellationToken); | ||||||
|  |         async public Task<List<TTuple>> ToListQfPrivateAsync<TTuple>(string sql, string field, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             var ret = new List<TTuple>(); |             var ret = new List<TTuple>(); | ||||||
|             if (_cancel?.Invoke() == true) return ret; |             if (_cancel?.Invoke() == true) return ret; | ||||||
|             var sql = this.ToSql(field); |             if (string.IsNullOrEmpty(sql)) return ret; | ||||||
|             var dbParms = _params.ToArray(); |             var dbParms = _params.ToArray(); | ||||||
|             var type = typeof(TTuple); |             var type = typeof(TTuple); | ||||||
|             var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); |             var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); | ||||||
| @@ -1380,12 +1310,41 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|  |  | ||||||
|         async protected Task<double> InternalAvgAsync(Expression exp, CancellationToken cancellationToken) |         async protected Task<double> InternalAvgAsync(Expression exp, CancellationToken cancellationToken) | ||||||
|         { |         { | ||||||
|             var list = await this.ToListAsync<double>($"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}", cancellationToken); |             var field = $"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) | ||||||
|  |             { | ||||||
|  |                 var list = await this.ToListAsync<double>(field, cancellationToken); | ||||||
|                 return list.Sum() / list.Count; |                 return list.Sum() / list.Count; | ||||||
|             } |             } | ||||||
|         async protected Task<TMember> InternalMaxAsync<TMember>(Expression exp, CancellationToken cancellationToken) => (await this.ToListAsync<TMember>($"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Max(); |  | ||||||
|         async protected Task<TMember> InternalMinAsync<TMember>(Expression exp, CancellationToken cancellationToken) => (await this.ToListAsync<TMember>($"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Min(); |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|         async protected Task<decimal> InternalSumAsync(Expression exp, CancellationToken cancellationToken) => (await this.ToListAsync<decimal>($"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Sum(); |             var list2 = await ToListQfPrivateAsync<double>(sql, field, cancellationToken); | ||||||
|  |             return list2.Sum() / list2.Count; | ||||||
|  |         } | ||||||
|  |         async protected Task<TMember> InternalMaxAsync<TMember>(Expression exp, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             var field = $"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) return (await this.ToListAsync<TMember>(field, cancellationToken)).Max(); | ||||||
|  |  | ||||||
|  |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|  |             return (await ToListQfPrivateAsync<TMember>(sql, field, cancellationToken)).Max(); | ||||||
|  |         } | ||||||
|  |         async protected Task<TMember> InternalMinAsync<TMember>(Expression exp, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             var field = $"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) return (await this.ToListAsync<TMember>(field, cancellationToken)).Min(); | ||||||
|  |  | ||||||
|  |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|  |             return (await ToListQfPrivateAsync<TMember>(sql, field, cancellationToken)).Min(); | ||||||
|  |         } | ||||||
|  |         async protected Task<decimal> InternalSumAsync(Expression exp, CancellationToken cancellationToken) | ||||||
|  |         { | ||||||
|  |             var field = $"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"; | ||||||
|  |             if (_limit <= 0 && _skip <= 0) return (await this.ToListAsync<decimal>(field, cancellationToken)).Sum(); | ||||||
|  |  | ||||||
|  |             var sql = GetNestSelectSql(exp, field, ToSql); | ||||||
|  |             return (await ToListQfPrivateAsync<decimal>(sql, field, cancellationToken)).Sum(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         static ConcurrentDictionary<Type, MethodInfo[]> _dicGetMethodsByName = new ConcurrentDictionary<Type, MethodInfo[]>(); |         static ConcurrentDictionary<Type, MethodInfo[]> _dicGetMethodsByName = new ConcurrentDictionary<Type, MethodInfo[]>(); | ||||||
|         async protected Task<List<TReturn>> InternalToListAsync<TReturn>(Expression select, CancellationToken cancellationToken) |         async protected Task<List<TReturn>> InternalToListAsync<TReturn>(Expression select, CancellationToken cancellationToken) | ||||||
| @@ -1577,34 +1536,7 @@ namespace FreeSql.Internal.CommonProvider | |||||||
|                 var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); |                 var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); | ||||||
|                 if (GetTableRuleUnions().Count <= 1) return (await this.ToListMapReaderAsync<TReturn>(af, cancellationToken)).FirstOrDefault(); |                 if (GetTableRuleUnions().Count <= 1) return (await this.ToListMapReaderAsync<TReturn>(af, cancellationToken)).FirstOrDefault(); | ||||||
|  |  | ||||||
|                 var affield = af.field; |                 var sql = GetNestSelectSql(select, af.field, ToSql); | ||||||
|                 var allMemExps = new FindAllMemberExpressionVisitor(this); |  | ||||||
|                 allMemExps.Visit(select); |  | ||||||
|                 field.Clear(); |  | ||||||
|                 var fieldAlias = new Dictionary<string, bool>(); |  | ||||||
|                 var fieldReplaced = new Dictionary<string, bool>(); |  | ||||||
|                 foreach (var memExp in allMemExps.Result) |  | ||||||
|                 { |  | ||||||
|                     var gef = GetExpressionField(memExp.Item1, FieldAliasOptions.AsProperty); |  | ||||||
|                     var geffield = gef.field; |  | ||||||
|                     if (fieldReplaced.ContainsKey(geffield)) continue; |  | ||||||
|                     fieldReplaced.Add(geffield, true); |  | ||||||
|  |  | ||||||
|                     field.Append(", ").Append(gef.field); |  | ||||||
|                     if (fieldAlias.ContainsKey(memExp.Item2.Attribute.Name)) |  | ||||||
|                     { |  | ||||||
|                         field.Append(_commonUtils.FieldAsAlias($"aas{fieldAlias.Count}")); |  | ||||||
|                         affield = affield.Replace(geffield, $"ftba.aas{fieldAlias.Count}"); |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         fieldAlias.Add(memExp.Item2.Attribute.Name, true); |  | ||||||
|                         affield = affield.Replace(geffield, $"ftba.{string.Join(".", geffield.Split('.').Skip(1))}"); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 var sql = this.ToSql(field.Remove(0, 2).ToString()); |  | ||||||
|                 sql = $"{_select} {affield} FROM ( \r\n    {sql.Replace("\r\n", "\r\n    ")}\r\n) ftba"; |  | ||||||
|                 return (await ToListMrPrivateAsync<TReturn>(sql, af, null, cancellationToken)).FirstOrDefault(); |                 return (await ToListMrPrivateAsync<TReturn>(sql, af, null, cancellationToken)).FirstOrDefault(); | ||||||
|             } |             } | ||||||
|             finally |             finally | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 2881099
					2881099