Skip to content

Commit

Permalink
Implement column-as keyword for 'count' aggregate with AsCountAs().
Browse files Browse the repository at this point in the history
  • Loading branch information
dgeelen-uipath committed Jul 2, 2021
1 parent 417f86e commit a6ae753
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 11 deletions.
59 changes: 56 additions & 3 deletions QueryBuilder.Tests/AggregateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,67 @@ public void Count()
Assert.Equal("SELECT COUNT(*) AS \"COUNT\" FROM \"A\"", c[EngineCodes.Firebird]);
}

[Fact]
public void CountAsStarAlias()
{
var query = new Query("A").AsCountAs("*", "Alias");

var c = Compile(query);

Assert.Equal("SELECT COUNT(*) AS [Alias] FROM [A]", c[EngineCodes.SqlServer]);
Assert.Equal("SELECT COUNT(*) AS `Alias` FROM `A`", c[EngineCodes.MySql]);
Assert.Equal("SELECT COUNT(*) AS \"Alias\" FROM \"A\"", c[EngineCodes.PostgreSql]);
Assert.Equal("SELECT COUNT(*) AS \"ALIAS\" FROM \"A\"", c[EngineCodes.Firebird]);
}

[Fact]
public void CountAsColumnAlias()
{
var query = new Query("A").AsCountAs("Column", "Alias");

var c = Compile(query);

Assert.Equal("SELECT COUNT([Column]) AS [Alias] FROM [A]", c[EngineCodes.SqlServer]);
Assert.Equal("SELECT COUNT(`Column`) AS `Alias` FROM `A`", c[EngineCodes.MySql]);
Assert.Equal("SELECT COUNT(\"Column\") AS \"Alias\" FROM \"A\"", c[EngineCodes.PostgreSql]);
Assert.Equal("SELECT COUNT(\"COLUMN\") AS \"ALIAS\" FROM \"A\"", c[EngineCodes.Firebird]);
}

[Fact]
public void CountDoesntModifyColumns()
{
{
var columns = new string[] { };
var query = new Query("A").AsCount(columns);
Compile(query);
Assert.Equal(columns, new string[] { });
}
{
var columns = new[] { "ColumnA", "ColumnB" };
var query = new Query("A").AsCount(columns);
Compile(query);
Assert.Equal(columns, new[] { "ColumnA", "ColumnB" });
}
}

[Fact]
public void CountMultipleColumns()
{
var query = new Query("A").AsCount(new[] { "ColumnA", "ColumnB" });

var c = Compile(query);

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]);
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]);
}

[Fact]
public void CountAsMultipleColumns()
{
var query = new Query("A").AsCountAs(new[] { "ColumnA", "ColumnB" }, "Alias");

var c = Compile(query);

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]);
}

[Fact]
Expand All @@ -43,7 +96,7 @@ public void DistinctCount()

var c = Compile(query);

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

[Fact]
Expand All @@ -53,7 +106,7 @@ public void DistinctCountMultipleColumns()

var c = Compile(query);

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

[Fact]
Expand Down
6 changes: 6 additions & 0 deletions QueryBuilder/Clauses/AggregateClause.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public class AggregateClause : AbstractClause
/// </value>
public List<string> Columns { get; set; }

/// <summary>
/// Gets or sets the alias of the result column.
/// </summary>
public string Alias { get; set; }

/// <summary>
/// Gets or sets the type of aggregate function.
/// </summary>
Expand All @@ -32,6 +37,7 @@ public override AbstractClause Clone()
Engine = Engine,
Type = Type,
Columns = new List<string>(Columns),
Alias = Alias,
Component = Component,
};
}
Expand Down
14 changes: 10 additions & 4 deletions QueryBuilder/Compilers/Compiler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

Expand Down Expand Up @@ -69,7 +70,7 @@ private Query TransformAggregateQuery(Query query)
{
query.ClearComponent("aggregate", EngineCode);
query.ClearComponent("select", EngineCode);
query.Select(clause.Columns.ToArray());
query.SelectAs(clause.Columns.Select(x => (x, null as string)).ToArray());
}
else
{
Expand All @@ -82,12 +83,14 @@ private Query TransformAggregateQuery(Query query)
var outerClause = new AggregateClause()
{
Columns = new List<string> { "*" },
Type = clause.Type
Type = clause.Type,
Alias = clause.Alias,
};

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

protected virtual SqlResult CompileRaw(Query query)
Expand Down Expand Up @@ -534,9 +537,12 @@ protected virtual string CompileColumns(SqlResult ctx)
sql = "DISTINCT " + sql;
}

return "SELECT " + aggregate.Type.ToUpperInvariant() + "(" + sql + $") {ColumnAsKeyword}" + WrapValue(aggregate.Type);
return $"SELECT {aggregate.Type.ToUpperInvariant()}({sql}) {ColumnAsKeyword}{WrapValue(aggregate.Alias ?? aggregate.Type)}";
}

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

Expand Down
15 changes: 11 additions & 4 deletions QueryBuilder/Query.Aggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace SqlKata
{
public partial class Query
{
public Query AsAggregate(string type, string[] columns = null)
public Query AsAggregate(string type, IEnumerable<string> columns, string alias = null)
{
if (columns.Count() == 0)
{
Expand All @@ -18,15 +18,16 @@ public Query AsAggregate(string type, string[] columns = null)
.AddComponent("aggregate", new AggregateClause
{
Type = type,
Columns = columns?.ToList() ?? new List<string>(),
Columns = columns.ToList(),
Alias = alias
});

return this;
}

public Query AsCount(string[] columns = null)
public Query AsCount(params string[] columns)
{
var cols = columns?.ToList() ?? new List<string> { };
var cols = columns.ToList();

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

public Query AsCountAs(string column, string alias) =>
AsAggregate("count", new string[] { column }, alias);

public Query AsCountAs(IEnumerable<string> columns, string alias) =>
AsAggregate("count", columns, alias);

public Query AsAvg(string column)
{
return AsAggregate("avg", new string[] { column });
Expand Down

0 comments on commit a6ae753

Please sign in to comment.