提交 03c2f221 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Use the same aggregate data for HISTOGRAM and MODE

上级 34385f44
...@@ -9,10 +9,12 @@ import java.util.ArrayList; ...@@ -9,10 +9,12 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map.Entry;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.dml.Select; import org.h2.command.dml.Select;
import org.h2.command.dml.SelectOrderBy; import org.h2.command.dml.SelectOrderBy;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn; import org.h2.expression.ExpressionColumn;
...@@ -29,6 +31,8 @@ import org.h2.table.Table; ...@@ -29,6 +31,8 @@ import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.util.ValueHashMap;
import org.h2.value.CompareMode;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueArray; import org.h2.value.ValueArray;
...@@ -445,7 +449,41 @@ public class Aggregate extends AbstractAggregate { ...@@ -445,7 +449,41 @@ public class Aggregate extends AbstractAggregate {
return d.getValue(db, dataType); return d.getValue(db, dataType);
} }
break; break;
case GROUP_CONCAT: { case HISTOGRAM:
return getHistogram(session, data);
case GROUP_CONCAT:
return getGroupConcat(session, data);
case ARRAY_AGG: {
Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) {
return ValueNull.INSTANCE;
}
if (orderByList != null || distinct) {
sortWithOrderBy(array);
}
if (orderByList != null) {
for (int i = 0; i < array.length; i++) {
array[i] = ((ValueArray) array[i]).getList()[0];
}
}
return ValueArray.get(array);
}
case MEDIAN: {
Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) {
return ValueNull.INSTANCE;
}
return AggregateMedian.median(session.getDatabase(), array, dataType);
}
case MODE:
return getMode(session, data);
default:
// Avoid compiler warning
}
return data.getValue(session.getDatabase(), dataType);
}
private Value getGroupConcat(Session session, AggregateData data) {
Value[] array = ((AggregateDataCollecting) data).getArray(); Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) { if (array == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
...@@ -472,38 +510,70 @@ public class Aggregate extends AbstractAggregate { ...@@ -472,38 +510,70 @@ public class Aggregate extends AbstractAggregate {
} }
return ValueString.get(buff.toString()); return ValueString.get(buff.toString());
} }
case ARRAY_AGG: {
Value[] array = ((AggregateDataCollecting) data).getArray(); private Value getHistogram(Session session, AggregateData data) {
if (array == null) { ValueHashMap<LongDataCounter> distinctValues = ((AggregateDataDistinctWithCounts) data).getValues();
return ValueNull.INSTANCE; if (distinctValues == null) {
return ValueArray.getEmpty();
} }
if (orderByList != null || distinct) { ValueArray[] values = new ValueArray[distinctValues.size()];
sortWithOrderBy(array); int i = 0;
for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
LongDataCounter d = entry.getValue();
values[i] = ValueArray.get(new Value[] { entry.getKey(), ValueLong.get(distinct ? 1L : d.count) });
i++;
}
Database db = session.getDatabase();
final Mode mode = db.getMode();
final CompareMode compareMode = db.getCompareMode();
Arrays.sort(values, new Comparator<ValueArray>() {
@Override
public int compare(ValueArray v1, ValueArray v2) {
Value a1 = v1.getList()[0];
Value a2 = v2.getList()[0];
return a1.compareTo(a2, mode, compareMode);
}
});
return ValueArray.get(values);
} }
private Value getMode(Session session, AggregateData data) {
Value v = ValueNull.INSTANCE;
ValueHashMap<LongDataCounter> distinctValues = ((AggregateDataDistinctWithCounts) data).getValues();
if (distinctValues == null) {
return v;
}
long count = 0L;
if (orderByList != null) { if (orderByList != null) {
for (int i = 0; i < array.length; i++) { boolean desc = (orderByList.get(0).sortType & SortOrder.DESCENDING) != 0;
array[i] = ((ValueArray) array[i]).getList()[0]; for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
long c = entry.getValue().count;
if (c > count) {
v = entry.getKey();
count = c;
} else if (c == count) {
Value v2 = entry.getKey();
int cmp = session.getDatabase().compareTypeSafe(v, v2);
if (desc) {
if (cmp >= 0) {
continue;
} }
} else if (cmp <= 0) {
continue;
} }
return ValueArray.get(array); v = v2;
} }
case MEDIAN: {
Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) {
return ValueNull.INSTANCE;
} }
return AggregateMedian.median(session.getDatabase(), array, dataType); } else {
for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
long c = entry.getValue().count;
if (c > count) {
v = entry.getKey();
count = c;
} }
case MODE:
if (orderByList != null) {
return ((AggregateDataMode) data).getOrderedValue(session.getDatabase(), dataType,
(orderByList.get(0).sortType & SortOrder.DESCENDING) != 0);
} }
//$FALL-THROUGH$
default:
// Avoid compiler warning
} }
return data.getValue(session.getDatabase(), dataType); return v.convertTo(dataType);
} }
@Override @Override
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.expression.aggregate; package org.h2.expression.aggregate;
import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.expression.aggregate.Aggregate.AggregateType; import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -55,9 +56,9 @@ abstract class AggregateData { ...@@ -55,9 +56,9 @@ abstract class AggregateData {
case SELECTIVITY: case SELECTIVITY:
return new AggregateDataSelectivity(distinct); return new AggregateDataSelectivity(distinct);
case HISTOGRAM: case HISTOGRAM:
return new AggregateDataHistogram(distinct); return new AggregateDataDistinctWithCounts(false, Constants.SELECTIVITY_DISTINCT_COUNT);
case MODE: case MODE:
return new AggregateDataMode(); return new AggregateDataDistinctWithCounts(true, Integer.MAX_VALUE);
case ENVELOPE: case ENVELOPE:
return new AggregateDataEnvelope(); return new AggregateDataEnvelope();
default: default:
......
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
import org.h2.engine.Database;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* Data stored while calculating an aggregate that needs distinct values with
* their counts.
*/
class AggregateDataDistinctWithCounts extends AggregateData {
private final boolean ignoreNulls;
private final int maxDistinctCount;
private ValueHashMap<LongDataCounter> values;
/**
* Creates new instance of data for aggregate that needs distinct values
* with their counts.
*
* @param ignoreNulls
* whether NULL values should be ignored
* @param maxDistinctCount
* maximum count of distinct values to collect
*/
AggregateDataDistinctWithCounts(boolean ignoreNulls, int maxDistinctCount) {
this.ignoreNulls = ignoreNulls;
this.maxDistinctCount = maxDistinctCount;
}
@Override
void add(Database database, int dataType, Value v) {
if (ignoreNulls && v == ValueNull.INSTANCE) {
return;
}
if (values == null) {
values = new ValueHashMap<>();
}
LongDataCounter a = values.get(v);
if (a == null) {
if (values.size() >= maxDistinctCount) {
return;
}
a = new LongDataCounter();
values.put(v, a);
}
a.count++;
}
@Override
Value getValue(Database database, int dataType) {
return null;
}
/**
* Returns map with values and their counts.
*
* @return map with values and their counts
*/
ValueHashMap<LongDataCounter> getValues() {
return values;
}
}
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map.Entry;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.util.ValueHashMap;
import org.h2.value.CompareMode;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueLong;
/**
* Data stored while calculating a HISTOGRAM aggregate.
*/
class AggregateDataHistogram extends AggregateData {
private final boolean distinct;
private ValueHashMap<LongDataCounter> distinctValues;
/**
* Creates new instance of data for HISTOGRAM aggregate.
*
* @param distinct if distinct is used
*/
AggregateDataHistogram(boolean distinct) {
this.distinct = distinct;
}
@Override
void add(Database database, int dataType, Value v) {
if (distinctValues == null) {
distinctValues = new ValueHashMap<>();
}
LongDataCounter a = distinctValues.get(v);
if (a == null) {
if (distinctValues.size() >= Constants.SELECTIVITY_DISTINCT_COUNT) {
return;
}
a = new LongDataCounter();
distinctValues.put(v, a);
}
a.count++;
}
@Override
Value getValue(Database database, int dataType) {
if (distinctValues == null) {
return ValueArray.getEmpty().convertTo(dataType);
}
ValueArray[] values = new ValueArray[distinctValues.size()];
int i = 0;
for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
LongDataCounter d = entry.getValue();
values[i] = ValueArray.get(new Value[] { entry.getKey(), ValueLong.get(distinct ? 1L : d.count) });
i++;
}
final Mode mode = database.getMode();
final CompareMode compareMode = database.getCompareMode();
Arrays.sort(values, new Comparator<ValueArray>() {
@Override
public int compare(ValueArray v1, ValueArray v2) {
Value a1 = v1.getList()[0];
Value a2 = v2.getList()[0];
return a1.compareTo(a2, mode, compareMode);
}
});
return ValueArray.get(values).convertTo(dataType);
}
}
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
import java.util.Map.Entry;
import org.h2.engine.Database;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* Data stored while calculating a MODE aggregate.
*/
class AggregateDataMode extends AggregateData {
private ValueHashMap<LongDataCounter> distinctValues;
@Override
void add(Database database, int dataType, Value v) {
if (v == ValueNull.INSTANCE) {
return;
}
if (distinctValues == null) {
distinctValues = new ValueHashMap<>();
}
LongDataCounter a = distinctValues.get(v);
if (a == null) {
a = new LongDataCounter();
distinctValues.put(v, a);
}
a.count++;
}
@Override
Value getValue(Database database, int dataType) {
Value v = ValueNull.INSTANCE;
if (distinctValues != null) {
long count = 0L;
for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
long c = entry.getValue().count;
if (c > count) {
v = entry.getKey();
count = c;
}
}
}
return v.convertTo(dataType);
}
Value getOrderedValue(Database database, int dataType, boolean desc) {
Value v = ValueNull.INSTANCE;
if (distinctValues != null) {
long count = 0L;
for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
long c = entry.getValue().count;
if (c > count) {
v = entry.getKey();
count = c;
} else if (c == count) {
Value v2 = entry.getKey();
int cmp = database.compareTypeSafe(v, v2);
if (desc) {
if (cmp >= 0) {
continue;
}
} else if (cmp <= 0) {
continue;
}
v = v2;
}
}
}
return v.convertTo(dataType);
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论