Skip to content

Commit a6ae753

Browse files
Implement column-as keyword for 'count' aggregate with AsCountAs().
1 parent 417f86e commit a6ae753

File tree

4 files changed

+83
-11
lines changed

4 files changed

+83
-11
lines changed

QueryBuilder.Tests/AggregateTests.cs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,67 @@ public void Count()
2626
Assert.Equal("SELECT COUNT(*) AS \"COUNT\" FROM \"A\"", c[EngineCodes.Firebird]);
2727
}
2828

29+
[Fact]
30+
public void CountAsStarAlias()
31+
{
32+
var query = new Query("A").AsCountAs("*", "Alias");
33+
34+
var c = Compile(query);
35+
36+
Assert.Equal("SELECT COUNT(*) AS [Alias] FROM [A]", c[EngineCodes.SqlServer]);
37+
Assert.Equal("SELECT COUNT(*) AS `Alias` FROM `A`", c[EngineCodes.MySql]);
38+
Assert.Equal("SELECT COUNT(*) AS \"Alias\" FROM \"A\"", c[EngineCodes.PostgreSql]);
39+
Assert.Equal("SELECT COUNT(*) AS \"ALIAS\" FROM \"A\"", c[EngineCodes.Firebird]);
40+
}
41+
42+
[Fact]
43+
public void CountAsColumnAlias()
44+
{
45+
var query = new Query("A").AsCountAs("Column", "Alias");
46+
47+
var c = Compile(query);
48+
49+
Assert.Equal("SELECT COUNT([Column]) AS [Alias] FROM [A]", c[EngineCodes.SqlServer]);
50+
Assert.Equal("SELECT COUNT(`Column`) AS `Alias` FROM `A`", c[EngineCodes.MySql]);
51+
Assert.Equal("SELECT COUNT(\"Column\") AS \"Alias\" FROM \"A\"", c[EngineCodes.PostgreSql]);
52+
Assert.Equal("SELECT COUNT(\"COLUMN\") AS \"ALIAS\" FROM \"A\"", c[EngineCodes.Firebird]);
53+
}
54+
55+
[Fact]
56+
public void CountDoesntModifyColumns()
57+
{
58+
{
59+
var columns = new string[] { };
60+
var query = new Query("A").AsCount(columns);
61+
Compile(query);
62+
Assert.Equal(columns, new string[] { });
63+
}
64+
{
65+
var columns = new[] { "ColumnA", "ColumnB" };
66+
var query = new Query("A").AsCount(columns);
67+
Compile(query);
68+
Assert.Equal(columns, new[] { "ColumnA", "ColumnB" });
69+
}
70+
}
71+
2972
[Fact]
3073
public void CountMultipleColumns()
3174
{
3275
var query = new Query("A").AsCount(new[] { "ColumnA", "ColumnB" });
3376

3477
var c = Compile(query);
3578

36-
Assert.Equal("SELECT COUNT(*) AS [count] FROM (SELECT 1 FROM [A] WHERE [ColumnA] IS NOT NULL AND [ColumnB] IS NOT NULL) AS [countQuery]", c[EngineCodes.SqlServer]);
79+
Assert.Equal("SELECT COUNT(*) AS [count] FROM (SELECT 1 FROM [A] WHERE [ColumnA] IS NOT NULL AND [ColumnB] IS NOT NULL) AS [CountQuery]", c[EngineCodes.SqlServer]);
80+
}
81+
82+
[Fact]
83+
public void CountAsMultipleColumns()
84+
{
85+
var query = new Query("A").AsCountAs(new[] { "ColumnA", "ColumnB" }, "Alias");
86+
87+
var c = Compile(query);
88+
89+
Assert.Equal("SELECT COUNT(*) AS [Alias] FROM (SELECT 1 FROM [A] WHERE [ColumnA] IS NOT NULL AND [ColumnB] IS NOT NULL) AS [AliasCountQuery]", c[EngineCodes.SqlServer]);
3790
}
3891

3992
[Fact]
@@ -43,7 +96,7 @@ public void DistinctCount()
4396

4497
var c = Compile(query);
4598

46-
Assert.Equal("SELECT COUNT(*) AS [count] FROM (SELECT DISTINCT * FROM [A]) AS [countQuery]", c[EngineCodes.SqlServer]);
99+
Assert.Equal("SELECT COUNT(*) AS [count] FROM (SELECT DISTINCT * FROM [A]) AS [CountQuery]", c[EngineCodes.SqlServer]);
47100
}
48101

49102
[Fact]
@@ -53,7 +106,7 @@ public void DistinctCountMultipleColumns()
53106

54107
var c = Compile(query);
55108

56-
Assert.Equal("SELECT COUNT(*) AS [count] FROM (SELECT DISTINCT [ColumnA], [ColumnB] FROM [A]) AS [countQuery]", c[EngineCodes.SqlServer]);
109+
Assert.Equal("SELECT COUNT(*) AS [count] FROM (SELECT DISTINCT [ColumnA], [ColumnB] FROM [A]) AS [CountQuery]", c[EngineCodes.SqlServer]);
57110
}
58111

59112
[Fact]

QueryBuilder/Clauses/AggregateClause.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public class AggregateClause : AbstractClause
1616
/// </value>
1717
public List<string> Columns { get; set; }
1818

19+
/// <summary>
20+
/// Gets or sets the alias of the result column.
21+
/// </summary>
22+
public string Alias { get; set; }
23+
1924
/// <summary>
2025
/// Gets or sets the type of aggregate function.
2126
/// </summary>
@@ -32,6 +37,7 @@ public override AbstractClause Clone()
3237
Engine = Engine,
3338
Type = Type,
3439
Columns = new List<string>(Columns),
40+
Alias = Alias,
3541
Component = Component,
3642
};
3743
}

QueryBuilder/Compilers/Compiler.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Linq;
45
using System.Text;
56

@@ -69,7 +70,7 @@ private Query TransformAggregateQuery(Query query)
6970
{
7071
query.ClearComponent("aggregate", EngineCode);
7172
query.ClearComponent("select", EngineCode);
72-
query.Select(clause.Columns.ToArray());
73+
query.SelectAs(clause.Columns.Select(x => (x, null as string)).ToArray());
7374
}
7475
else
7576
{
@@ -82,12 +83,14 @@ private Query TransformAggregateQuery(Query query)
8283
var outerClause = new AggregateClause()
8384
{
8485
Columns = new List<string> { "*" },
85-
Type = clause.Type
86+
Type = clause.Type,
87+
Alias = clause.Alias,
8688
};
8789

8890
return new Query()
8991
.AddComponent("aggregate", outerClause)
90-
.From(query, $"{clause.Type}Query");
92+
// Use alias + capitalized type + 'query' as alias
93+
.From(query, $"{clause.Alias}{clause.Type.First().ToString().ToUpperInvariant()}{clause.Type.Substring(1)}Query");
9194
}
9295

9396
protected virtual SqlResult CompileRaw(Query query)
@@ -534,9 +537,12 @@ protected virtual string CompileColumns(SqlResult ctx)
534537
sql = "DISTINCT " + sql;
535538
}
536539

537-
return "SELECT " + aggregate.Type.ToUpperInvariant() + "(" + sql + $") {ColumnAsKeyword}" + WrapValue(aggregate.Type);
540+
return $"SELECT {aggregate.Type.ToUpperInvariant()}({sql}) {ColumnAsKeyword}{WrapValue(aggregate.Alias ?? aggregate.Type)}";
538541
}
539542

543+
// Counts of multiple columns are implemented by a sub-query
544+
// which selects 1 from every non-null record. E.g.
545+
// SELECT COUNT(*) FROM (SELECT 1 FROM [A] WHERE [ColumnA] IS NOT NULL AND [ColumnB] IS NOT NULL)
540546
return "SELECT 1";
541547
}
542548

QueryBuilder/Query.Aggregate.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace SqlKata
55
{
66
public partial class Query
77
{
8-
public Query AsAggregate(string type, string[] columns = null)
8+
public Query AsAggregate(string type, IEnumerable<string> columns, string alias = null)
99
{
1010
if (columns.Count() == 0)
1111
{
@@ -18,15 +18,16 @@ public Query AsAggregate(string type, string[] columns = null)
1818
.AddComponent("aggregate", new AggregateClause
1919
{
2020
Type = type,
21-
Columns = columns?.ToList() ?? new List<string>(),
21+
Columns = columns.ToList(),
22+
Alias = alias
2223
});
2324

2425
return this;
2526
}
2627

27-
public Query AsCount(string[] columns = null)
28+
public Query AsCount(params string[] columns)
2829
{
29-
var cols = columns?.ToList() ?? new List<string> { };
30+
var cols = columns.ToList();
3031

3132
if (!cols.Any())
3233
{
@@ -36,6 +37,12 @@ public Query AsCount(string[] columns = null)
3637
return AsAggregate("count", cols.ToArray());
3738
}
3839

40+
public Query AsCountAs(string column, string alias) =>
41+
AsAggregate("count", new string[] { column }, alias);
42+
43+
public Query AsCountAs(IEnumerable<string> columns, string alias) =>
44+
AsAggregate("count", columns, alias);
45+
3946
public Query AsAvg(string column)
4047
{
4148
return AsAggregate("avg", new string[] { column });

0 commit comments

Comments
 (0)