Skip to content

Commit e54b22c

Browse files
2881028810
authored andcommitted
- 增加 SqlExt.GroupConcat MySql 函数解决;
1 parent 73eb3c8 commit e54b22c

File tree

4 files changed

+263
-41
lines changed

4 files changed

+263
-41
lines changed

FreeSql.Tests/FreeSql.Tests/UnitTest3.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,37 @@ public Author123(long id, long songId)
155155
[Fact]
156156
public void Test03()
157157
{
158+
var sqlextGroupConcat = g.mysql.Select<Edi, EdiItem>()
159+
.InnerJoin((a, b) => b.Id == a.Id)
160+
.ToSql((a, b) => new
161+
{
162+
Id = a.Id,
163+
EdiId = b.Id,
164+
case1 = SqlExt.Case()
165+
.When(a.Id == 1, 10)
166+
.When(a.Id == 2, 11)
167+
.When(a.Id == 3, 12)
168+
.When(a.Id == 4, 13)
169+
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
170+
.End(),
171+
groupct1 = SqlExt.GroupConcat(a.Id).Distinct().OrderBy(b.EdiId).Separator("_").ToValue()
172+
});
173+
var sqlextGroupConcatToList = g.mysql.Select<Edi, EdiItem>()
174+
.InnerJoin((a, b) => b.Id == a.Id)
175+
.ToList((a, b) => new
176+
{
177+
Id = a.Id,
178+
EdiId = b.Id,
179+
case1 = SqlExt.Case()
180+
.When(a.Id == 1, 10)
181+
.When(a.Id == 2, 11)
182+
.When(a.Id == 3, 12)
183+
.When(a.Id == 4, 13)
184+
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
185+
.End(),
186+
groupct1 = SqlExt.GroupConcat(a.Id).Distinct().OrderBy(b.EdiId).Separator("_").ToValue()
187+
});
188+
158189
var sqlextCase = g.sqlserver.Select<Edi, EdiItem>()
159190
.InnerJoin((a, b) => b.Id == a.Id)
160191
.ToSql((a, b) => new
@@ -167,7 +198,8 @@ public void Test03()
167198
.When(a.Id == 3, 12)
168199
.When(a.Id == 4, 13)
169200
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
170-
.End()
201+
.End(),
202+
over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(),
171203
});
172204
var sqlextCaseToList = g.sqlserver.Select<Edi, EdiItem>()
173205
.InnerJoin((a, b) => b.Id == a.Id)
@@ -181,7 +213,8 @@ public void Test03()
181213
.When(a.Id == 3, 12)
182214
.When(a.Id == 4, 13)
183215
.When(a.Id == 5, SqlExt.Case().When(b.Id == 1, 10000).Else(999).End())
184-
.End()
216+
.End(),
217+
over1 = SqlExt.Rank().Over().OrderBy(a.Id).OrderByDescending(b.EdiId).ToValue(),
185218
});
186219

187220

FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs

Lines changed: 156 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Text;
66
using System.Threading;
7+
using static FreeSql.SqlExtExtensions;
78

89
[ExpressionCall]
910
public static class FreeSqlGlobalExpressionCallExtensions
@@ -46,99 +47,215 @@ public static bool BetweenEnd(this DateTime that, DateTime start, DateTime end)
4647

4748
namespace FreeSql
4849
{
50+
/// <summary>
51+
/// SqlExt 是利用自定表达式函数解析功能,解析默认常用的SQL函数,欢迎 PR
52+
/// </summary>
4953
[ExpressionCall]
5054
public static class SqlExt
5155
{
52-
public static ThreadLocal<ExpressionCallContext> expContext = new ThreadLocal<ExpressionCallContext>();
56+
internal static ThreadLocal<ExpressionCallContext> expContext = new ThreadLocal<ExpressionCallContext>();
5357

54-
public static ISqlOver<long> Rank() => Over<long>("RANK()");
55-
public static ISqlOver<long> DenseRank() => Over<long>("DENSE_RANK()");
56-
public static ISqlOver<long> Count() => Over<long>("COUNT()");
57-
public static ISqlOver<decimal> Sum(object column) => Over<decimal>($"Sum({expContext.Value.ParsedContent["column"]})");
58-
public static ISqlOver<decimal> Avg() => Over<decimal>($"AVG({expContext.Value.ParsedContent["column"]})");
59-
public static ISqlOver<T> Max<T>(T column) => Over<T>($"MAX({expContext.Value.ParsedContent["column"]})");
60-
public static ISqlOver<T> Min<T>(T column) => Over<T>($"MIN({expContext.Value.ParsedContent["column"]})");
61-
public static ISqlOver<long> RowNumber() => Over<long>("ROW_NUMBER()");
58+
#region SqlServer/PostgreSQL over
59+
/// <summary>
60+
/// rank() over(order by ...)
61+
/// </summary>
62+
/// <returns></returns>
63+
public static ISqlOver<long> Rank() => Over<long>("rank()");
64+
/// <summary>
65+
/// dense_rank() over(order by ...)
66+
/// </summary>
67+
/// <returns></returns>
68+
public static ISqlOver<long> DenseRank() => Over<long>("dense_rank()");
69+
/// <summary>
70+
/// count() over(order by ...)
71+
/// </summary>
72+
/// <returns></returns>
73+
public static ISqlOver<long> Count() => Over<long>("count()");
74+
/// <summary>
75+
/// sum(..) over(order by ...)
76+
/// </summary>
77+
/// <param name="column"></param>
78+
/// <returns></returns>
79+
public static ISqlOver<decimal> Sum(object column) => Over<decimal>($"sum({expContext.Value.ParsedContent["column"]})");
80+
/// <summary>
81+
/// avg(..) over(order by ...)
82+
/// </summary>
83+
/// <returns></returns>
84+
public static ISqlOver<decimal> Avg() => Over<decimal>($"avg({expContext.Value.ParsedContent["column"]})");
85+
/// <summary>
86+
/// max(..) over(order by ...)
87+
/// </summary>
88+
/// <typeparam name="T"></typeparam>
89+
/// <param name="column"></param>
90+
/// <returns></returns>
91+
public static ISqlOver<T> Max<T>(T column) => Over<T>($"max({expContext.Value.ParsedContent["column"]})");
92+
/// <summary>
93+
/// min(..) over(order by ...)
94+
/// </summary>
95+
/// <typeparam name="T"></typeparam>
96+
/// <param name="column"></param>
97+
/// <returns></returns>
98+
public static ISqlOver<T> Min<T>(T column) => Over<T>($"min({expContext.Value.ParsedContent["column"]})");
99+
/// <summary>
100+
/// SqlServer row_number() over(order by ...)
101+
/// </summary>
102+
/// <returns></returns>
103+
public static ISqlOver<long> RowNumber() => Over<long>("row_number()");
104+
#endregion
105+
106+
/// <summary>
107+
/// case when .. then .. end
108+
/// </summary>
109+
/// <returns></returns>
110+
public static ICaseWhenEnd Case() => SqlExtExtensions.Case();
111+
/// <summary>
112+
/// MySql group_concat(distinct .. order by .. separator ..)
113+
/// </summary>
114+
/// <param name="column"></param>
115+
/// <returns></returns>
116+
public static IGroupConcat GroupConcat(object column) => SqlExtExtensions.GroupConcat(column);
117+
}
118+
119+
[ExpressionCall]
120+
public static class SqlExtExtensions //这个类存在的意义,是不想使用者方法名污染
121+
{
122+
static ThreadLocal<ExpressionCallContext> expContextSelf = new ThreadLocal<ExpressionCallContext>();
123+
static ExpressionCallContext expContext => expContextSelf.Value ?? SqlExt.expContext.Value;
124+
internal static ThreadLocal<List<ExpSbInfo>> expSb = new ThreadLocal<List<ExpSbInfo>>();
125+
internal static ExpSbInfo expSbLast => expSb.Value.Last();
126+
internal class ExpSbInfo
127+
{
128+
public StringBuilder Sb { get; } = new StringBuilder();
129+
public bool IsOrderBy = false;
130+
public bool IsDistinct = false;
131+
}
62132

63133
#region .. over([partition by ..] order by ...)
64-
static ThreadLocal<StringBuilder> expOverSb = new ThreadLocal<StringBuilder>();
65-
static ThreadLocal<bool> expOverSbIsOrderBy = new ThreadLocal<bool>();
66-
static ISqlOver<TValue> Over<TValue>(string sqlFunc)
134+
internal static ISqlOver<TValue> Over<TValue>(string sqlFunc)
67135
{
68-
expOverSb.Value = new StringBuilder();
69-
expOverSbIsOrderBy.Value = false;
70-
expOverSb.Value.Append($"{sqlFunc} ");
136+
if (expSb.Value == null) expSb.Value = new List<ExpSbInfo>();
137+
expSb.Value.Add(new ExpSbInfo());
138+
expSbLast.Sb.Append(sqlFunc).Append(" ");
71139
return null;
72140
}
73141
public static ISqlOver<TValue> Over<TValue>(this ISqlOver<TValue> that)
74142
{
75-
expOverSb.Value.Append("OVER(");
143+
expSbLast.Sb.Append("over(");
76144
return that;
77145
}
78146
public static ISqlOver<TValue> PartitionBy<TValue>(this ISqlOver<TValue> that, object column)
79147
{
80-
expOverSb.Value.Append("PARTITION BY ").Append(expContext.Value.ParsedContent["column"]).Append(",");
148+
expSbLast.Sb.Append(" partition by ").Append(expContext.ParsedContent["column"]).Append(",");
81149
return that;
82150
}
83-
public static ISqlOver<TValue> OrderBy<TValue>(this ISqlOver<TValue> that, object column) => OrderBy(that, false);
84-
public static ISqlOver<TValue> OrderByDescending<TValue>(this ISqlOver<TValue> that, object column) => OrderBy(that, true);
85-
static ISqlOver<TValue> OrderBy<TValue>(this ISqlOver<TValue> that, bool isDesc)
151+
public static ISqlOver<TValue> OrderBy<TValue>(this ISqlOver<TValue> that, object column) => OrderByPriv(that, false);
152+
public static ISqlOver<TValue> OrderByDescending<TValue>(this ISqlOver<TValue> that, object column) => OrderByPriv(that, true);
153+
static ISqlOver<TValue> OrderByPriv<TValue>(this ISqlOver<TValue> that, bool isDesc)
86154
{
87-
var sb = expOverSb.Value;
88-
if (expOverSbIsOrderBy.Value == false)
155+
var sb = expSbLast.Sb;
156+
if (expSbLast.IsOrderBy == false)
89157
{
90-
sb.Append("ORDER BY ");
91-
expOverSbIsOrderBy.Value = true;
158+
sb.Append(" order by ");
159+
expSbLast.IsOrderBy = true;
92160
}
93-
sb.Append(expContext.Value.ParsedContent["column"]);
161+
sb.Append(expContext.ParsedContent["column"]);
94162
if (isDesc) sb.Append(" desc");
95163
sb.Append(",");
96164
return that;
97165
}
98166
public static TValue ToValue<TValue>(this ISqlOver<TValue> that)
99167
{
100-
var sb = expOverSb.Value.ToString().TrimEnd(',');
101-
expOverSb.Value.Clear();
102-
expContext.Value.Result = $"{sb})";
168+
var sql = expSbLast.Sb.ToString().TrimEnd(',');
169+
expSbLast.Sb.Clear();
170+
expSb.Value.RemoveAt(expSb.Value.Count - 1);
171+
expContext.Result = $"{sql})";
103172
return default;
104173
}
105174
public interface ISqlOver<TValue> { }
106175
#endregion
107176

108177
#region case when .. then .. when .. then .. end
109-
static ThreadLocal<List<StringBuilder>> expCaseWhenEndSb = new ThreadLocal<List<StringBuilder>>();
110178
public static ICaseWhenEnd Case()
111179
{
112-
if (expCaseWhenEndSb.Value == null) expCaseWhenEndSb.Value = new List<StringBuilder>();
113-
expCaseWhenEndSb.Value.Add(new StringBuilder().Append("CASE "));
180+
if (expSb.Value == null) expSb.Value = new List<ExpSbInfo>();
181+
expSb.Value.Add(new ExpSbInfo());
182+
expSbLast.Sb.Append("case ");
114183
return null;
115184
}
116185
public static ICaseWhenEnd<TValue> When<TValue>(this ICaseWhenEnd that, bool test, TValue then)
117186
{
118-
expCaseWhenEndSb.Value.Last().Append("\r\n WHEN ").Append(expContext.Value.ParsedContent["test"]).Append(" THEN ").Append(expContext.Value.ParsedContent["then"]);
187+
expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}when ").Append(expContext.ParsedContent["test"]).Append(" then ").Append(expContext.ParsedContent["then"]);
119188
return null;
120189
}
121190
public static ICaseWhenEnd<TValue> When<TValue>(this ICaseWhenEnd<TValue> that, bool test, TValue then)
122191
{
123-
expCaseWhenEndSb.Value.Last().Append("\r\n WHEN ").Append(expContext.Value.ParsedContent["test"]).Append(" THEN ").Append(expContext.Value.ParsedContent["then"]);
192+
expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}when ").Append(expContext.ParsedContent["test"]).Append(" then ").Append(expContext.ParsedContent["then"]);
124193
return null;
125194
}
126195
public static ICaseWhenEnd<TValue> Else<TValue>(this ICaseWhenEnd<TValue> that, TValue then)
127196
{
128-
expCaseWhenEndSb.Value.Last().Append("\r\n ELSE ").Append(expContext.Value.ParsedContent["then"]);
197+
expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}else ").Append(expContext.ParsedContent["then"]);
129198
return null;
130199
}
131200
public static TValue End<TValue>(this ICaseWhenEnd<TValue> that)
132201
{
133-
var sb = expCaseWhenEndSb.Value;
134-
var sql = sb.Last().Append("\r\nEND").ToString();
135-
sb.Last().Clear();
136-
sb.RemoveAt(sb.Count - 1);
137-
expContext.Value.Result = sql;
202+
var sql = expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2 - 2)}end").ToString();
203+
expSbLast.Sb.Clear();
204+
expSb.Value.RemoveAt(expSb.Value.Count - 1);
205+
expContext.Result = sql;
138206
return default;
139207
}
140208
public interface ICaseWhenEnd { }
141209
public interface ICaseWhenEnd<TValue> { }
142210
#endregion
211+
212+
#region group_concat
213+
public static IGroupConcat GroupConcat(object column)
214+
{
215+
if (expSb.Value == null) expSb.Value = new List<ExpSbInfo>();
216+
expSb.Value.Add(new ExpSbInfo());
217+
expSbLast.Sb.Append("group_concat(").Append(expContext.ParsedContent["column"]);
218+
return null;
219+
}
220+
public static IGroupConcat Distinct(this IGroupConcat that)
221+
{
222+
if (expSbLast.IsDistinct == false)
223+
{
224+
expSbLast.Sb.Insert(expSbLast.Sb.ToString().LastIndexOf("group_concat(") + 13, "distinct ");
225+
expSbLast.IsDistinct = true;
226+
}
227+
return that;
228+
}
229+
public static IGroupConcat Separator(this IGroupConcat that, object separator)
230+
{
231+
if (expSbLast.IsOrderBy) expSbLast.Sb.Remove(expSbLast.Sb.Length - 1, 1);
232+
expSbLast.Sb.Append(" separator ").Append(expContext.ParsedContent["separator"]);
233+
return that;
234+
}
235+
public static IGroupConcat OrderBy(this IGroupConcat that, object column) => OrderByPriv(that, false);
236+
public static IGroupConcat OrderByDescending(this IGroupConcat that, object column) => OrderByPriv(that, true);
237+
static IGroupConcat OrderByPriv(this IGroupConcat that, bool isDesc)
238+
{
239+
var sb = expSbLast.Sb;
240+
if (expSbLast.IsOrderBy == false)
241+
{
242+
sb.Append(" order by ");
243+
expSbLast.IsOrderBy = true;
244+
}
245+
sb.Append(expContext.ParsedContent["column"]);
246+
if (isDesc) sb.Append(" desc");
247+
sb.Append(",");
248+
return that;
249+
}
250+
public static string ToValue(this IGroupConcat that)
251+
{
252+
var sql = expSbLast.Sb.ToString().TrimEnd(',');
253+
expSbLast.Sb.Clear();
254+
expSb.Value.RemoveAt(expSb.Value.Count - 1);
255+
expContext.Result = $"{sql})";
256+
return default;
257+
}
258+
public interface IGroupConcat { }
259+
#endregion
143260
}
144261
}

FreeSql/Extensions/FreeSqlGlobalExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ public static object CreateInstanceGetDefaultValue(this Type that)
130130
if (that == typeof(string)) return default(string);
131131
if (that == typeof(Guid)) return default(Guid);
132132
if (that.IsArray) return Array.CreateInstance(that, 0);
133+
if (that.IsInterface || that.IsAbstract) return null;
133134
var ctorParms = that.InternalGetTypeConstructor0OrFirst(false)?.GetParameters();
134135
if (ctorParms == null || ctorParms.Any() == false) return Activator.CreateInstance(that, true);
135136
return Activator.CreateInstance(that, ctorParms

0 commit comments

Comments
 (0)