Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit c296e0d

Browse files
committed
Aggregate functions support for JoinBuilder
Aggregate functions support for JoinBuilder
1 parent 0aae0d4 commit c296e0d

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

src/ServiceStack.OrmLite/JoinSqlBuilder.cs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class JoinSqlBuilder<TNewPoco, TBasePoco>
1111
private List<KeyValuePair<string, WhereType>> whereList = new List<KeyValuePair<string, WhereType>>();
1212
private List<string> columnList = new List<string>();
1313
private List<KeyValuePair<string, bool>> orderByList = new List<KeyValuePair<string, bool>>();
14+
private bool isDistinct = false;
15+
private bool isAggregateUsed = false;
1416

1517
private string baseTableName = "";
1618
private Type basePocoType;
@@ -38,6 +40,21 @@ private List<string> ColumnList<T>(string tableName, Expression<Func<T, object>>
3840
return result;
3941
}
4042

43+
private List<string> ColumnList<T>(bool withTablePrefix = true)
44+
{
45+
var pocoType = typeof(T);
46+
var tableName = pocoType.GetModelDefinition().ModelName;
47+
List<string> result = new List<string>(pocoType.GetModelDefinition().FieldDefinitions.Count);
48+
foreach (var item in pocoType.GetModelDefinition().FieldDefinitions)
49+
{
50+
if (withTablePrefix)
51+
result.Add(string.Format("{0}.{1}", OrmLiteConfig.DialectProvider.GetQuotedTableName(tableName), OrmLiteConfig.DialectProvider.GetQuotedColumnName(item.Name)));
52+
else
53+
result.Add(string.Format("{0}", OrmLiteConfig.DialectProvider.GetQuotedColumnName(item.Name)));
54+
}
55+
return result;
56+
}
57+
4158
private void ProcessUnary(string tableName, UnaryExpression u, List<string> lst, bool withTablePrefix )
4259
{
4360
if (u.NodeType == ExpressionType.Convert)
@@ -110,6 +127,74 @@ public JoinSqlBuilder<TNewPoco, TBasePoco> Select<T>(Expression<Func<T, object>>
110127
return this;
111128
}
112129

130+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectAll<T>()
131+
{
132+
Type associatedType = this.PreviousAssociatedType(typeof(T), typeof(T));
133+
if (associatedType == null)
134+
{
135+
throw new Exception("Either the source or destination table should be associated ");
136+
}
137+
this.columnList.AddRange(ColumnList<T>());
138+
return this;
139+
}
140+
141+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectDistinct()
142+
{
143+
isDistinct = true;
144+
return this;
145+
}
146+
147+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectMax<T>(Expression<Func<T, object>> selectColumn)
148+
{
149+
return SelectGenericAggregate<T>(selectColumn, "MAX");
150+
}
151+
152+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectMin<T>(Expression<Func<T, object>> selectColumn)
153+
{
154+
return SelectGenericAggregate<T>(selectColumn, "MIN");
155+
}
156+
157+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectCount<T>(Expression<Func<T, object>> selectColumn)
158+
{
159+
return SelectGenericAggregate<T>(selectColumn, "COUNT");
160+
}
161+
162+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectAverage<T>(Expression<Func<T, object>> selectColumn)
163+
{
164+
return SelectGenericAggregate<T>(selectColumn, "AVG");
165+
}
166+
167+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectSum<T>(Expression<Func<T, object>> selectColumn)
168+
{
169+
return SelectGenericAggregate<T>(selectColumn, "SUM");
170+
}
171+
172+
private JoinSqlBuilder<TNewPoco, TBasePoco> SelectGenericAggregate<T>(Expression<Func<T, object>> selectColumn, string functionName)
173+
{
174+
Type associatedType = this.PreviousAssociatedType(typeof(T), typeof(T));
175+
if (associatedType == null)
176+
{
177+
throw new Exception("Either the source or destination table should be associated ");
178+
}
179+
isAggregateUsed = true;
180+
181+
CheckAggregateUsage(true);
182+
183+
var columns = ColumnList(associatedType.GetModelDefinition().ModelName, selectColumn);
184+
if ((columns.Count == 0) || (columns.Count > 1))
185+
{
186+
throw new Exception("Expression should select only one Column ");
187+
}
188+
this.columnList.Add(string.Format(" {0}({1}) ",functionName.ToUpper(),columns[0]));
189+
return this;
190+
}
191+
192+
public JoinSqlBuilder<TNewPoco, TBasePoco> SelectMin()
193+
{
194+
isDistinct = true;
195+
return this;
196+
}
197+
113198
public JoinSqlBuilder<TNewPoco, TBasePoco> Where<T>(Expression<Func<T, bool>> where)
114199
{
115200
return WhereInternal(WhereType.AND, where);
@@ -298,22 +383,38 @@ private Type PreviousAssociatedType(Type sourceTableType, Type destinationTableT
298383
return null;
299384
}
300385

386+
private void CheckAggregateUsage(bool ignoreCurrentItem)
387+
{
388+
if ((columnList.Count > ( ignoreCurrentItem ? 0 : 1)) && (isAggregateUsed == true))
389+
{
390+
throw new Exception("Aggregate function cannot be used with non aggregate select columns");
391+
}
392+
}
393+
301394
public string ToSql()
302395
{
396+
CheckAggregateUsage(false);
397+
303398
var sb = new StringBuilder();
304399
sb.Append("SELECT ");
305400

306401
var colSB = new StringBuilder();
307402

308403
if (columnList.Count > 0)
309404
{
405+
if (isDistinct)
406+
sb.Append(" DISTINCT ");
407+
310408
foreach (var col in columnList)
311409
{
312410
colSB.AppendFormat("{0}{1}", colSB.Length > 0 ? "," : "", col);
313411
}
314412
}
315413
else
316414
{
415+
if (isDistinct && typeof(TNewPoco).GetModelDefinition().FieldDefinitions.Count > 0)
416+
sb.Append(" DISTINCT ");
417+
317418
foreach ( var fi in typeof(TNewPoco).GetModelDefinition().FieldDefinitions)
318419
{
319420
colSB.AppendFormat("{0}{1}", colSB.Length > 0 ? "," : "", String.IsNullOrEmpty(fi.BelongToModelName) ? (fi.FieldName) : ((OrmLiteConfig.DialectProvider.GetQuotedTableName(fi.BelongToModelName) + "." + OrmLiteConfig.DialectProvider.GetQuotedColumnName(fi.FieldName))));

src/SqliteExpressionsTest/JoinTest.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,91 @@ public static void Test()
121121
var sql2 = jn.ToSql();
122122
var item = db.QuerySingle<UserEx>(sql2);
123123

124+
jn.Clear();
125+
jn = new JoinSqlBuilder<UserEx, User>();
126+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id)
127+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id)
128+
.OrderByDescending<User>(x=>x.Name)
129+
.OrderBy<User>(x=>x.Id)
130+
.SelectAll<UserData>()
131+
.Where<User>(x=> x.Id == 0);
132+
133+
var sql3 = jn.ToSql();
134+
var items3 = db.Query<UserEx>(sql3);
135+
136+
jn.Clear();
137+
jn = new JoinSqlBuilder<UserEx, User>();
138+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id, x => new { x.Name, x.Id }, x => new { x.UserDataValue })
139+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id, null, x => new { x.ServiceName })
140+
.OrderByDescending<User>(x=>x.Name)
141+
.OrderBy<User>(x=>x.Id)
142+
.SelectDistinct()
143+
.SelectAll<UserData>()
144+
.Where<User>(x=> x.Id == 0);
145+
146+
var sql4 = jn.ToSql();
147+
var items4 = db.Query<UserEx>(sql4);
148+
149+
jn.Clear();
150+
jn = new JoinSqlBuilder<UserEx, User>();
151+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id)
152+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id)
153+
.OrderByDescending<User>(x=>x.Name)
154+
.OrderBy<User>(x=>x.Id)
155+
.SelectCount<User>(x=>x.Id)
156+
.Where<User>(x=> x.Id == 0);
157+
158+
var sql5 = jn.ToSql();
159+
var items5 = db.GetScalar<long>(sql5);
160+
161+
jn.Clear();
162+
jn = new JoinSqlBuilder<UserEx, User>();
163+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id)
164+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id)
165+
.OrderByDescending<User>(x => x.Name)
166+
.OrderBy<User>(x=>x.Id)
167+
.SelectMax<User>(x=>x.Id)
168+
.Where<User>(x=> x.Id == 0);
169+
170+
var sql6 = jn.ToSql();
171+
var items6 = db.GetScalar<long>(sql6);
172+
173+
jn.Clear();
174+
jn = new JoinSqlBuilder<UserEx, User>();
175+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id)
176+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id)
177+
.OrderByDescending<User>(x => x.Name)
178+
.OrderBy<User>(x=>x.Id)
179+
.SelectMin<User>(x=>x.Id)
180+
.Where<User>(x=> x.Id == 0);
181+
182+
var sql7 = jn.ToSql();
183+
var items7 = db.GetScalar<long>(sql7);
184+
185+
jn.Clear();
186+
jn = new JoinSqlBuilder<UserEx, User>();
187+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id)
188+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id)
189+
.OrderByDescending<User>(x => x.Name)
190+
.OrderBy<User>(x=>x.Id)
191+
.SelectAverage<User>(x=>x.Id)
192+
.Where<User>(x=> x.Id == 0);
193+
194+
var sql8 = jn.ToSql();
195+
var items8 = db.GetScalar<long>(sql8);
196+
197+
jn.Clear();
198+
jn = new JoinSqlBuilder<UserEx, User>();
199+
jn = jn.Join<User, UserData>(x => x.UserDataId, x => x.Id)
200+
.LeftJoin<User, UserService>(x => x.UserServiceId, x => x.Id)
201+
.OrderByDescending<User>(x => x.Name)
202+
.OrderBy<User>(x=>x.Id)
203+
.SelectSum<User>(x=>x.Id)
204+
.Where<User>(x=> x.Id == 0);
205+
206+
var sql9 = jn.ToSql();
207+
var items9 = db.GetScalar<long>(sql9);
208+
124209
}
125210

126211
File.Delete(path);

0 commit comments

Comments
 (0)