a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
217 lines
5.5 KiB
C#
217 lines
5.5 KiB
C#
//
|
|
// Aggregation.cs
|
|
//
|
|
// Author:
|
|
// Juraj Skripsky (juraj@hotfeet.ch)
|
|
//
|
|
// (C) 2004 HotFeet GmbH (http://www.hotfeet.ch)
|
|
//
|
|
|
|
//
|
|
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Data;
|
|
|
|
namespace Mono.Data.SqlExpressions {
|
|
internal enum AggregationFunction {
|
|
Count, Sum, Min, Max, Avg, StDev, Var
|
|
}
|
|
|
|
internal class Aggregation : BaseExpression {
|
|
bool cacheResults;
|
|
DataRow[] rows;
|
|
ColumnReference column;
|
|
AggregationFunction function;
|
|
int count;
|
|
IConvertible result;
|
|
DataRowChangeEventHandler RowChangeHandler;
|
|
DataTable table ;
|
|
|
|
public Aggregation (bool cacheResults, DataRow[] rows, AggregationFunction function, ColumnReference column)
|
|
{
|
|
this.cacheResults = cacheResults;
|
|
this.rows = rows;
|
|
this.column = column;
|
|
this.function = function;
|
|
this.result = null;
|
|
if (cacheResults)
|
|
RowChangeHandler = new DataRowChangeEventHandler (InvalidateCache);
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (!base.Equals (obj))
|
|
return false;
|
|
|
|
if (!(obj is Aggregation))
|
|
return false;
|
|
|
|
Aggregation other = (Aggregation) obj;
|
|
if (!other.function.Equals( function))
|
|
return false;
|
|
|
|
if (!other.column.Equals (column))
|
|
return false;
|
|
|
|
if (other.rows != null && rows != null) {
|
|
if (other.rows.Length != rows.Length)
|
|
return false;
|
|
|
|
for (int i=0; i < rows.Length; i++)
|
|
if (other.rows [i] != rows [i])
|
|
return false;
|
|
|
|
}
|
|
else if (!(other.rows == null && rows == null))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
int hashCode = base.GetHashCode ();
|
|
hashCode ^= function.GetHashCode ();
|
|
hashCode ^= column.GetHashCode ();
|
|
for (int i=0; i < rows.Length; i++)
|
|
hashCode ^= rows [i].GetHashCode ();
|
|
|
|
return hashCode;
|
|
}
|
|
|
|
|
|
public override object Eval (DataRow row)
|
|
{
|
|
//TODO: implement a better caching strategy and a mechanism for cache invalidation.
|
|
//for now only aggregation over the table owning 'row' (e.g. 'sum(parts)'
|
|
//in constrast to 'sum(child.parts)') is cached.
|
|
if (cacheResults && result != null && column.ReferencedTable == ReferencedTable.Self)
|
|
return result;
|
|
|
|
count = 0;
|
|
result = null;
|
|
|
|
object[] values;
|
|
if (rows == null)
|
|
values = column.GetValues (column.GetReferencedRows (row));
|
|
else
|
|
values = column.GetValues (rows);
|
|
|
|
foreach (object val in values) {
|
|
if (val == null)
|
|
continue;
|
|
|
|
count++;
|
|
Aggregate ((IConvertible)val);
|
|
}
|
|
|
|
switch (function) {
|
|
case AggregationFunction.StDev:
|
|
case AggregationFunction.Var:
|
|
result = CalcStatisticalFunction (values);
|
|
break;
|
|
|
|
case AggregationFunction.Avg:
|
|
result = ((count == 0) ? DBNull.Value : Numeric.Divide (result, count));
|
|
break;
|
|
|
|
case AggregationFunction.Count:
|
|
result = count;
|
|
break;
|
|
}
|
|
|
|
if (result == null)
|
|
result = DBNull.Value;
|
|
|
|
if (cacheResults && column.ReferencedTable == ReferencedTable.Self)
|
|
{
|
|
table = row.Table;
|
|
row.Table.RowChanged += RowChangeHandler;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
override public bool DependsOn(DataColumn other)
|
|
{
|
|
return column.DependsOn(other);
|
|
}
|
|
|
|
private void Aggregate (IConvertible val)
|
|
{
|
|
switch (function) {
|
|
case AggregationFunction.Min:
|
|
result = (result != null ? Numeric.Min (result, val) : val);
|
|
return;
|
|
|
|
case AggregationFunction.Max:
|
|
result = (result != null ? Numeric.Max (result, val) : val);
|
|
return;
|
|
|
|
case AggregationFunction.Sum:
|
|
case AggregationFunction.Avg:
|
|
case AggregationFunction.StDev:
|
|
case AggregationFunction.Var:
|
|
result = (result != null ? Numeric.Add (result, val) : val);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private IConvertible CalcStatisticalFunction (object[] values)
|
|
{
|
|
if (count < 2)
|
|
return DBNull.Value;
|
|
|
|
double average = (double)Convert.ChangeType(result, TypeCode.Double) / count;
|
|
double res = 0.0;
|
|
|
|
foreach (object val in values) {
|
|
if (val == null)
|
|
continue;
|
|
|
|
double diff = average - (double)Convert.ChangeType(val, TypeCode.Double);
|
|
res += System.Math.Pow (diff, 2);
|
|
}
|
|
res /= (count - 1);
|
|
|
|
if (function == AggregationFunction.StDev)
|
|
res = System.Math.Sqrt (res);
|
|
|
|
return res;
|
|
}
|
|
|
|
public override void ResetExpression ()
|
|
{
|
|
if (table != null)
|
|
InvalidateCache (table, null);
|
|
}
|
|
|
|
private void InvalidateCache (Object sender, DataRowChangeEventArgs args)
|
|
{
|
|
result = null;
|
|
((DataTable)sender).RowChanged -= RowChangeHandler;
|
|
}
|
|
}
|
|
}
|