From e54b22cee54f75e339de6c144a70eda303e4dfba Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Tue, 23 Jun 2020 17:12:16 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20SqlExt.GroupConcat=20M?= =?UTF-8?q?ySql=20=E5=87=BD=E6=95=B0=E8=A7=A3=E5=86=B3=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.Tests/FreeSql.Tests/UnitTest3.cs | 37 +++- .../FreeSqlGlobalExpressionCallExtensions.cs | 195 ++++++++++++++---- FreeSql/Extensions/FreeSqlGlobalExtensions.cs | 1 + FreeSql/FreeSql.xml | 71 +++++++ 4 files changed, 263 insertions(+), 41 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs index a0be59b2..54f51f72 100644 --- a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs +++ b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs @@ -155,6 +155,37 @@ namespace FreeSql.Tests [Fact] public void Test03() { + var sqlextGroupConcat = g.mysql.Select() + .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() + .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() .InnerJoin((a, b) => b.Id == a.Id) .ToSql((a, b) => new @@ -167,7 +198,8 @@ namespace FreeSql.Tests .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() + .End(), + over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(), }); var sqlextCaseToList = g.sqlserver.Select() .InnerJoin((a, b) => b.Id == a.Id) @@ -181,7 +213,8 @@ namespace FreeSql.Tests .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() + .End(), + over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(), }); diff --git a/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs index 7fe955da..abbcb35e 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; +using static FreeSql.SqlExtExtensions; [ExpressionCall] public static class FreeSqlGlobalExpressionCallExtensions @@ -46,99 +47,215 @@ public static class FreeSqlGlobalExpressionCallExtensions namespace FreeSql { + /// + /// SqlExt 是利用自定表达式函数解析功能,解析默认常用的SQL函数,欢迎 PR + /// [ExpressionCall] public static class SqlExt { - public static ThreadLocal expContext = new ThreadLocal(); + internal static ThreadLocal expContext = new ThreadLocal(); - public static ISqlOver Rank() => Over("RANK()"); - public static ISqlOver DenseRank() => Over("DENSE_RANK()"); - public static ISqlOver Count() => Over("COUNT()"); - public static ISqlOver Sum(object column) => Over($"Sum({expContext.Value.ParsedContent["column"]})"); - public static ISqlOver Avg() => Over($"AVG({expContext.Value.ParsedContent["column"]})"); - public static ISqlOver Max(T column) => Over($"MAX({expContext.Value.ParsedContent["column"]})"); - public static ISqlOver Min(T column) => Over($"MIN({expContext.Value.ParsedContent["column"]})"); - public static ISqlOver RowNumber() => Over("ROW_NUMBER()"); + #region SqlServer/PostgreSQL over + /// + /// rank() over(order by ...) + /// + /// + public static ISqlOver Rank() => Over("rank()"); + /// + /// dense_rank() over(order by ...) + /// + /// + public static ISqlOver DenseRank() => Over("dense_rank()"); + /// + /// count() over(order by ...) + /// + /// + public static ISqlOver Count() => Over("count()"); + /// + /// sum(..) over(order by ...) + /// + /// + /// + public static ISqlOver Sum(object column) => Over($"sum({expContext.Value.ParsedContent["column"]})"); + /// + /// avg(..) over(order by ...) + /// + /// + public static ISqlOver Avg() => Over($"avg({expContext.Value.ParsedContent["column"]})"); + /// + /// max(..) over(order by ...) + /// + /// + /// + /// + public static ISqlOver Max(T column) => Over($"max({expContext.Value.ParsedContent["column"]})"); + /// + /// min(..) over(order by ...) + /// + /// + /// + /// + public static ISqlOver Min(T column) => Over($"min({expContext.Value.ParsedContent["column"]})"); + /// + /// SqlServer row_number() over(order by ...) + /// + /// + public static ISqlOver RowNumber() => Over("row_number()"); + #endregion + + /// + /// case when .. then .. end + /// + /// + public static ICaseWhenEnd Case() => SqlExtExtensions.Case(); + /// + /// MySql group_concat(distinct .. order by .. separator ..) + /// + /// + /// + public static IGroupConcat GroupConcat(object column) => SqlExtExtensions.GroupConcat(column); + } + + [ExpressionCall] + public static class SqlExtExtensions //这个类存在的意义,是不想使用者方法名污染 + { + static ThreadLocal expContextSelf = new ThreadLocal(); + static ExpressionCallContext expContext => expContextSelf.Value ?? SqlExt.expContext.Value; + internal static ThreadLocal> expSb = new ThreadLocal>(); + 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 ...) - static ThreadLocal expOverSb = new ThreadLocal(); - static ThreadLocal expOverSbIsOrderBy = new ThreadLocal(); - static ISqlOver Over(string sqlFunc) + internal static ISqlOver Over(string sqlFunc) { - expOverSb.Value = new StringBuilder(); - expOverSbIsOrderBy.Value = false; - expOverSb.Value.Append($"{sqlFunc} "); + if (expSb.Value == null) expSb.Value = new List(); + expSb.Value.Add(new ExpSbInfo()); + expSbLast.Sb.Append(sqlFunc).Append(" "); return null; } public static ISqlOver Over(this ISqlOver that) { - expOverSb.Value.Append("OVER("); + expSbLast.Sb.Append("over("); return that; } public static ISqlOver PartitionBy(this ISqlOver 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; } - public static ISqlOver OrderBy(this ISqlOver that, object column) => OrderBy(that, false); - public static ISqlOver OrderByDescending(this ISqlOver that, object column) => OrderBy(that, true); - static ISqlOver OrderBy(this ISqlOver that, bool isDesc) + public static ISqlOver OrderBy(this ISqlOver that, object column) => OrderByPriv(that, false); + public static ISqlOver OrderByDescending(this ISqlOver that, object column) => OrderByPriv(that, true); + static ISqlOver OrderByPriv(this ISqlOver that, bool isDesc) { - var sb = expOverSb.Value; - if (expOverSbIsOrderBy.Value == false) + var sb = expSbLast.Sb; + if (expSbLast.IsOrderBy == false) { - sb.Append("ORDER BY "); - expOverSbIsOrderBy.Value = true; + sb.Append(" order by "); + expSbLast.IsOrderBy = true; } - sb.Append(expContext.Value.ParsedContent["column"]); + sb.Append(expContext.ParsedContent["column"]); if (isDesc) sb.Append(" desc"); sb.Append(","); return that; } public static TValue ToValue(this ISqlOver that) { - var sb = expOverSb.Value.ToString().TrimEnd(','); - expOverSb.Value.Clear(); - expContext.Value.Result = $"{sb})"; + var sql = expSbLast.Sb.ToString().TrimEnd(','); + expSbLast.Sb.Clear(); + expSb.Value.RemoveAt(expSb.Value.Count - 1); + expContext.Result = $"{sql})"; return default; } public interface ISqlOver { } #endregion #region case when .. then .. when .. then .. end - static ThreadLocal> expCaseWhenEndSb = new ThreadLocal>(); public static ICaseWhenEnd Case() { - if (expCaseWhenEndSb.Value == null) expCaseWhenEndSb.Value = new List(); - expCaseWhenEndSb.Value.Add(new StringBuilder().Append("CASE ")); + if (expSb.Value == null) expSb.Value = new List(); + expSb.Value.Add(new ExpSbInfo()); + expSbLast.Sb.Append("case "); return null; } public static ICaseWhenEnd When(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; } public static ICaseWhenEnd When(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; } public static ICaseWhenEnd Else(this ICaseWhenEnd 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; } public static TValue End(this ICaseWhenEnd that) { - var sb = expCaseWhenEndSb.Value; - var sql = sb.Last().Append("\r\nEND").ToString(); - sb.Last().Clear(); - sb.RemoveAt(sb.Count - 1); - expContext.Value.Result = sql; + var sql = expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2 - 2)}end").ToString(); + expSbLast.Sb.Clear(); + expSb.Value.RemoveAt(expSb.Value.Count - 1); + expContext.Result = sql; return default; } public interface ICaseWhenEnd { } public interface ICaseWhenEnd { } #endregion + + #region group_concat + public static IGroupConcat GroupConcat(object column) + { + if (expSb.Value == null) expSb.Value = new List(); + 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 } } \ No newline at end of file diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index 9537f680..ab1b955e 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -130,6 +130,7 @@ public static partial class FreeSqlGlobalExtensions if (that == typeof(string)) return default(string); if (that == typeof(Guid)) return default(Guid); if (that.IsArray) return Array.CreateInstance(that, 0); + if (that.IsInterface || that.IsAbstract) return null; var ctorParms = that.InternalGetTypeConstructor0OrFirst(false)?.GetParameters(); if (ctorParms == null || ctorParms.Any() == false) return Activator.CreateInstance(that, true); return Activator.CreateInstance(that, ctorParms diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index b749f144..5d1ff11c 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -684,6 +684,77 @@ + + + SqlExt 是利用自定表达式函数解析功能,解析默认常用的SQL函数,欢迎 PR + + + + + rank() over(order by ...) + + + + + + dense_rank() over(order by ...) + + + + + + count() over(order by ...) + + + + + + sum(..) over(order by ...) + + + + + + + avg(..) over(order by ...) + + + + + + max(..) over(order by ...) + + + + + + + + min(..) over(order by ...) + + + + + + + + SqlServer row_number() over(order by ...) + + + + + + case when .. then .. end + + + + + + MySql group_concat(distinct .. order by .. separator ..) + + + + 使用连接串(推荐)