- 增加 SqlExt.GroupConcat MySql 函数解决;

This commit is contained in:
28810 2020-06-23 17:12:16 +08:00
parent 73eb3c8b21
commit e54b22cee5
4 changed files with 263 additions and 41 deletions

View File

@ -155,6 +155,37 @@ namespace FreeSql.Tests
[Fact] [Fact]
public void Test03() public void Test03()
{ {
var sqlextGroupConcat = g.mysql.Select<Edi, EdiItem>()
.InnerJoin((a, b) => b.Id == a.Id)
.ToSql((a, b) => new
{
Id = a.Id,
EdiId = b.Id,
case1 = SqlExt.Case()
.When(a.Id == 1, 10)
.When(a.Id == 2, 11)
.When(a.Id == 3, 12)
.When(a.Id == 4, 13)
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
.End(),
groupct1 = SqlExt.GroupConcat(a.Id).Distinct().OrderBy(b.EdiId).Separator("_").ToValue()
});
var sqlextGroupConcatToList = g.mysql.Select<Edi, EdiItem>()
.InnerJoin((a, b) => b.Id == a.Id)
.ToList((a, b) => new
{
Id = a.Id,
EdiId = b.Id,
case1 = SqlExt.Case()
.When(a.Id == 1, 10)
.When(a.Id == 2, 11)
.When(a.Id == 3, 12)
.When(a.Id == 4, 13)
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
.End(),
groupct1 = SqlExt.GroupConcat(a.Id).Distinct().OrderBy(b.EdiId).Separator("_").ToValue()
});
var sqlextCase = g.sqlserver.Select<Edi, EdiItem>() var sqlextCase = g.sqlserver.Select<Edi, EdiItem>()
.InnerJoin((a, b) => b.Id == a.Id) .InnerJoin((a, b) => b.Id == a.Id)
.ToSql((a, b) => new .ToSql((a, b) => new
@ -167,7 +198,8 @@ namespace FreeSql.Tests
.When(a.Id == 3, 12) .When(a.Id == 3, 12)
.When(a.Id == 4, 13) .When(a.Id == 4, 13)
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End()) .When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
.End() .End(),
over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(),
}); });
var sqlextCaseToList = g.sqlserver.Select<Edi, EdiItem>() var sqlextCaseToList = g.sqlserver.Select<Edi, EdiItem>()
.InnerJoin((a, b) => b.Id == a.Id) .InnerJoin((a, b) => b.Id == a.Id)
@ -181,7 +213,8 @@ namespace FreeSql.Tests
.When(a.Id == 3, 12) .When(a.Id == 3, 12)
.When(a.Id == 4, 13) .When(a.Id == 4, 13)
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End()) .When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
.End() .End(),
over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(),
}); });

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using static FreeSql.SqlExtExtensions;
[ExpressionCall] [ExpressionCall]
public static class FreeSqlGlobalExpressionCallExtensions public static class FreeSqlGlobalExpressionCallExtensions
@ -46,99 +47,215 @@ public static class FreeSqlGlobalExpressionCallExtensions
namespace FreeSql namespace FreeSql
{ {
/// <summary>
/// SqlExt 是利用自定表达式函数解析功能解析默认常用的SQL函数欢迎 PR
/// </summary>
[ExpressionCall] [ExpressionCall]
public static class SqlExt public static class SqlExt
{ {
public static ThreadLocal<ExpressionCallContext> expContext = new ThreadLocal<ExpressionCallContext>(); internal static ThreadLocal<ExpressionCallContext> expContext = new ThreadLocal<ExpressionCallContext>();
public static ISqlOver<long> Rank() => Over<long>("RANK()"); #region SqlServer/PostgreSQL over
public static ISqlOver<long> DenseRank() => Over<long>("DENSE_RANK()"); /// <summary>
public static ISqlOver<long> Count() => Over<long>("COUNT()"); /// rank() over(order by ...)
public static ISqlOver<decimal> Sum(object column) => Over<decimal>($"Sum({expContext.Value.ParsedContent["column"]})"); /// </summary>
public static ISqlOver<decimal> Avg() => Over<decimal>($"AVG({expContext.Value.ParsedContent["column"]})"); /// <returns></returns>
public static ISqlOver<T> Max<T>(T column) => Over<T>($"MAX({expContext.Value.ParsedContent["column"]})"); public static ISqlOver<long> Rank() => Over<long>("rank()");
public static ISqlOver<T> Min<T>(T column) => Over<T>($"MIN({expContext.Value.ParsedContent["column"]})"); /// <summary>
public static ISqlOver<long> RowNumber() => Over<long>("ROW_NUMBER()"); /// dense_rank() over(order by ...)
/// </summary>
/// <returns></returns>
public static ISqlOver<long> DenseRank() => Over<long>("dense_rank()");
/// <summary>
/// count() over(order by ...)
/// </summary>
/// <returns></returns>
public static ISqlOver<long> Count() => Over<long>("count()");
/// <summary>
/// sum(..) over(order by ...)
/// </summary>
/// <param name="column"></param>
/// <returns></returns>
public static ISqlOver<decimal> Sum(object column) => Over<decimal>($"sum({expContext.Value.ParsedContent["column"]})");
/// <summary>
/// avg(..) over(order by ...)
/// </summary>
/// <returns></returns>
public static ISqlOver<decimal> Avg() => Over<decimal>($"avg({expContext.Value.ParsedContent["column"]})");
/// <summary>
/// max(..) over(order by ...)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="column"></param>
/// <returns></returns>
public static ISqlOver<T> Max<T>(T column) => Over<T>($"max({expContext.Value.ParsedContent["column"]})");
/// <summary>
/// min(..) over(order by ...)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="column"></param>
/// <returns></returns>
public static ISqlOver<T> Min<T>(T column) => Over<T>($"min({expContext.Value.ParsedContent["column"]})");
/// <summary>
/// SqlServer row_number() over(order by ...)
/// </summary>
/// <returns></returns>
public static ISqlOver<long> RowNumber() => Over<long>("row_number()");
#endregion
/// <summary>
/// case when .. then .. end
/// </summary>
/// <returns></returns>
public static ICaseWhenEnd Case() => SqlExtExtensions.Case();
/// <summary>
/// MySql group_concat(distinct .. order by .. separator ..)
/// </summary>
/// <param name="column"></param>
/// <returns></returns>
public static IGroupConcat GroupConcat(object column) => SqlExtExtensions.GroupConcat(column);
}
[ExpressionCall]
public static class SqlExtExtensions //这个类存在的意义,是不想使用者方法名污染
{
static ThreadLocal<ExpressionCallContext> expContextSelf = new ThreadLocal<ExpressionCallContext>();
static ExpressionCallContext expContext => expContextSelf.Value ?? SqlExt.expContext.Value;
internal static ThreadLocal<List<ExpSbInfo>> expSb = new ThreadLocal<List<ExpSbInfo>>();
internal static ExpSbInfo expSbLast => expSb.Value.Last();
internal class ExpSbInfo
{
public StringBuilder Sb { get; } = new StringBuilder();
public bool IsOrderBy = false;
public bool IsDistinct = false;
}
#region .. over([partition by ..] order by ...) #region .. over([partition by ..] order by ...)
static ThreadLocal<StringBuilder> expOverSb = new ThreadLocal<StringBuilder>(); internal static ISqlOver<TValue> Over<TValue>(string sqlFunc)
static ThreadLocal<bool> expOverSbIsOrderBy = new ThreadLocal<bool>();
static ISqlOver<TValue> Over<TValue>(string sqlFunc)
{ {
expOverSb.Value = new StringBuilder(); if (expSb.Value == null) expSb.Value = new List<ExpSbInfo>();
expOverSbIsOrderBy.Value = false; expSb.Value.Add(new ExpSbInfo());
expOverSb.Value.Append($"{sqlFunc} "); expSbLast.Sb.Append(sqlFunc).Append(" ");
return null; return null;
} }
public static ISqlOver<TValue> Over<TValue>(this ISqlOver<TValue> that) public static ISqlOver<TValue> Over<TValue>(this ISqlOver<TValue> that)
{ {
expOverSb.Value.Append("OVER("); expSbLast.Sb.Append("over(");
return that; return that;
} }
public static ISqlOver<TValue> PartitionBy<TValue>(this ISqlOver<TValue> that, object column) public static ISqlOver<TValue> PartitionBy<TValue>(this ISqlOver<TValue> that, object column)
{ {
expOverSb.Value.Append("PARTITION BY ").Append(expContext.Value.ParsedContent["column"]).Append(","); expSbLast.Sb.Append(" partition by ").Append(expContext.ParsedContent["column"]).Append(",");
return that; return that;
} }
public static ISqlOver<TValue> OrderBy<TValue>(this ISqlOver<TValue> that, object column) => OrderBy(that, false); public static ISqlOver<TValue> OrderBy<TValue>(this ISqlOver<TValue> that, object column) => OrderByPriv(that, false);
public static ISqlOver<TValue> OrderByDescending<TValue>(this ISqlOver<TValue> that, object column) => OrderBy(that, true); public static ISqlOver<TValue> OrderByDescending<TValue>(this ISqlOver<TValue> that, object column) => OrderByPriv(that, true);
static ISqlOver<TValue> OrderBy<TValue>(this ISqlOver<TValue> that, bool isDesc) static ISqlOver<TValue> OrderByPriv<TValue>(this ISqlOver<TValue> that, bool isDesc)
{ {
var sb = expOverSb.Value; var sb = expSbLast.Sb;
if (expOverSbIsOrderBy.Value == false) if (expSbLast.IsOrderBy == false)
{ {
sb.Append("ORDER BY "); sb.Append(" order by ");
expOverSbIsOrderBy.Value = true; expSbLast.IsOrderBy = true;
} }
sb.Append(expContext.Value.ParsedContent["column"]); sb.Append(expContext.ParsedContent["column"]);
if (isDesc) sb.Append(" desc"); if (isDesc) sb.Append(" desc");
sb.Append(","); sb.Append(",");
return that; return that;
} }
public static TValue ToValue<TValue>(this ISqlOver<TValue> that) public static TValue ToValue<TValue>(this ISqlOver<TValue> that)
{ {
var sb = expOverSb.Value.ToString().TrimEnd(','); var sql = expSbLast.Sb.ToString().TrimEnd(',');
expOverSb.Value.Clear(); expSbLast.Sb.Clear();
expContext.Value.Result = $"{sb})"; expSb.Value.RemoveAt(expSb.Value.Count - 1);
expContext.Result = $"{sql})";
return default; return default;
} }
public interface ISqlOver<TValue> { } public interface ISqlOver<TValue> { }
#endregion #endregion
#region case when .. then .. when .. then .. end #region case when .. then .. when .. then .. end
static ThreadLocal<List<StringBuilder>> expCaseWhenEndSb = new ThreadLocal<List<StringBuilder>>();
public static ICaseWhenEnd Case() public static ICaseWhenEnd Case()
{ {
if (expCaseWhenEndSb.Value == null) expCaseWhenEndSb.Value = new List<StringBuilder>(); if (expSb.Value == null) expSb.Value = new List<ExpSbInfo>();
expCaseWhenEndSb.Value.Add(new StringBuilder().Append("CASE ")); expSb.Value.Add(new ExpSbInfo());
expSbLast.Sb.Append("case ");
return null; return null;
} }
public static ICaseWhenEnd<TValue> When<TValue>(this ICaseWhenEnd that, bool test, TValue then) public static ICaseWhenEnd<TValue> When<TValue>(this ICaseWhenEnd that, bool test, TValue then)
{ {
expCaseWhenEndSb.Value.Last().Append("\r\n WHEN ").Append(expContext.Value.ParsedContent["test"]).Append(" THEN ").Append(expContext.Value.ParsedContent["then"]); expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}when ").Append(expContext.ParsedContent["test"]).Append(" then ").Append(expContext.ParsedContent["then"]);
return null; return null;
} }
public static ICaseWhenEnd<TValue> When<TValue>(this ICaseWhenEnd<TValue> that, bool test, TValue then) public static ICaseWhenEnd<TValue> When<TValue>(this ICaseWhenEnd<TValue> that, bool test, TValue then)
{ {
expCaseWhenEndSb.Value.Last().Append("\r\n WHEN ").Append(expContext.Value.ParsedContent["test"]).Append(" THEN ").Append(expContext.Value.ParsedContent["then"]); expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}when ").Append(expContext.ParsedContent["test"]).Append(" then ").Append(expContext.ParsedContent["then"]);
return null; return null;
} }
public static ICaseWhenEnd<TValue> Else<TValue>(this ICaseWhenEnd<TValue> that, TValue then) public static ICaseWhenEnd<TValue> Else<TValue>(this ICaseWhenEnd<TValue> that, TValue then)
{ {
expCaseWhenEndSb.Value.Last().Append("\r\n ELSE ").Append(expContext.Value.ParsedContent["then"]); expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}else ").Append(expContext.ParsedContent["then"]);
return null; return null;
} }
public static TValue End<TValue>(this ICaseWhenEnd<TValue> that) public static TValue End<TValue>(this ICaseWhenEnd<TValue> that)
{ {
var sb = expCaseWhenEndSb.Value; var sql = expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2 - 2)}end").ToString();
var sql = sb.Last().Append("\r\nEND").ToString(); expSbLast.Sb.Clear();
sb.Last().Clear(); expSb.Value.RemoveAt(expSb.Value.Count - 1);
sb.RemoveAt(sb.Count - 1); expContext.Result = sql;
expContext.Value.Result = sql;
return default; return default;
} }
public interface ICaseWhenEnd { } public interface ICaseWhenEnd { }
public interface ICaseWhenEnd<TValue> { } public interface ICaseWhenEnd<TValue> { }
#endregion #endregion
#region group_concat
public static IGroupConcat GroupConcat(object column)
{
if (expSb.Value == null) expSb.Value = new List<ExpSbInfo>();
expSb.Value.Add(new ExpSbInfo());
expSbLast.Sb.Append("group_concat(").Append(expContext.ParsedContent["column"]);
return null;
}
public static IGroupConcat Distinct(this IGroupConcat that)
{
if (expSbLast.IsDistinct == false)
{
expSbLast.Sb.Insert(expSbLast.Sb.ToString().LastIndexOf("group_concat(") + 13, "distinct ");
expSbLast.IsDistinct = true;
}
return that;
}
public static IGroupConcat Separator(this IGroupConcat that, object separator)
{
if (expSbLast.IsOrderBy) expSbLast.Sb.Remove(expSbLast.Sb.Length - 1, 1);
expSbLast.Sb.Append(" separator ").Append(expContext.ParsedContent["separator"]);
return that;
}
public static IGroupConcat OrderBy(this IGroupConcat that, object column) => OrderByPriv(that, false);
public static IGroupConcat OrderByDescending(this IGroupConcat that, object column) => OrderByPriv(that, true);
static IGroupConcat OrderByPriv(this IGroupConcat that, bool isDesc)
{
var sb = expSbLast.Sb;
if (expSbLast.IsOrderBy == false)
{
sb.Append(" order by ");
expSbLast.IsOrderBy = true;
}
sb.Append(expContext.ParsedContent["column"]);
if (isDesc) sb.Append(" desc");
sb.Append(",");
return that;
}
public static string ToValue(this IGroupConcat that)
{
var sql = expSbLast.Sb.ToString().TrimEnd(',');
expSbLast.Sb.Clear();
expSb.Value.RemoveAt(expSb.Value.Count - 1);
expContext.Result = $"{sql})";
return default;
}
public interface IGroupConcat { }
#endregion
} }
} }

View File

@ -130,6 +130,7 @@ public static partial class FreeSqlGlobalExtensions
if (that == typeof(string)) return default(string); if (that == typeof(string)) return default(string);
if (that == typeof(Guid)) return default(Guid); if (that == typeof(Guid)) return default(Guid);
if (that.IsArray) return Array.CreateInstance(that, 0); if (that.IsArray) return Array.CreateInstance(that, 0);
if (that.IsInterface || that.IsAbstract) return null;
var ctorParms = that.InternalGetTypeConstructor0OrFirst(false)?.GetParameters(); var ctorParms = that.InternalGetTypeConstructor0OrFirst(false)?.GetParameters();
if (ctorParms == null || ctorParms.Any() == false) return Activator.CreateInstance(that, true); if (ctorParms == null || ctorParms.Any() == false) return Activator.CreateInstance(that, true);
return Activator.CreateInstance(that, ctorParms return Activator.CreateInstance(that, ctorParms

View File

@ -684,6 +684,77 @@
<param name="columnType"></param> <param name="columnType"></param>
<param name="setExp"></param> <param name="setExp"></param>
</member> </member>
<member name="T:FreeSql.SqlExt">
<summary>
SqlExt 是利用自定表达式函数解析功能解析默认常用的SQL函数欢迎 PR
</summary>
</member>
<member name="M:FreeSql.SqlExt.Rank">
<summary>
rank() over(order by ...)
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.DenseRank">
<summary>
dense_rank() over(order by ...)
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.Count">
<summary>
count() over(order by ...)
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.Sum(System.Object)">
<summary>
sum(..) over(order by ...)
</summary>
<param name="column"></param>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.Avg">
<summary>
avg(..) over(order by ...)
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.Max``1(``0)">
<summary>
max(..) over(order by ...)
</summary>
<typeparam name="T"></typeparam>
<param name="column"></param>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.Min``1(``0)">
<summary>
min(..) over(order by ...)
</summary>
<typeparam name="T"></typeparam>
<param name="column"></param>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.RowNumber">
<summary>
SqlServer row_number() over(order by ...)
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.Case">
<summary>
case when .. then .. end
</summary>
<returns></returns>
</member>
<member name="M:FreeSql.SqlExt.GroupConcat(System.Object)">
<summary>
MySql group_concat(distinct .. order by .. separator ..)
</summary>
<param name="column"></param>
<returns></returns>
</member>
<member name="M:FreeSql.FreeSqlBuilder.UseConnectionString(FreeSql.DataType,System.String,System.Type)"> <member name="M:FreeSql.FreeSqlBuilder.UseConnectionString(FreeSql.DataType,System.String,System.Type)">
<summary> <summary>
使用连接串(推荐) 使用连接串(推荐)