mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 18:52:50 +08:00
- 优化 Limit + Sum/Avg/Max/Min 为嵌套查询;
This commit is contained in:
parent
3b961681dc
commit
27cb11e4a7
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user