Unverified 提交 d0c4469a authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1408 from katzyn/aggregate

Add MODE() aggregate function
...@@ -3416,6 +3416,27 @@ Aggregates are only allowed in select statements. ...@@ -3416,6 +3416,27 @@ Aggregates are only allowed in select statements.
MEDIAN(X) MEDIAN(X)
" "
"Functions (Aggregate)","MODE","
{ MODE( value ) [ ORDER BY expression [ ASC | DESC ] ] }
| { MODE() WITHIN GROUP(ORDER BY expression [ ASC | DESC ]) }
[ FILTER ( WHERE expression ) ]
","
Returns the value that occurs with the greatest frequency.
If there are multiple values with the same frequency only one value will be returned.
In this situation value will be chosen based on optional ORDER BY clause
that should specify exactly the same expression as argument of this function.
Use ascending order to get smallest value or descending order to get largest value
from multiple values with the same frequency.
If this clause is not specified the exact chosen value is not determined in this situation.
NULL values are ignored in the calculation.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
MODE(X)
MODE(X ORDER BY X)
MODE() WITHIN GROUP(ORDER BY X)
"
"Functions (Numeric)","ABS"," "Functions (Numeric)","ABS","
ABS ( { numeric } ) ABS ( { numeric } )
"," ","
......
...@@ -21,6 +21,14 @@ Change Log ...@@ -21,6 +21,14 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #1407: Add a MODE() aggregate function
</li>
<li>PR #1402: Duplicate conditions in column check constraint
</li>
<li>PR #1399: Add more subclasses of SQLException and use it for some error codes
</li>
<li>PR #1397: Add DATEADD return type detection
</li>
<li>Issue #1393: Add INFORMATION_SCHEMA.COLUMNS.IS_VISIBLE <li>Issue #1393: Add INFORMATION_SCHEMA.COLUMNS.IS_VISIBLE
</li> </li>
<li>PR #1392: Some refactoring and assorted minor optimizations <li>PR #1392: Some refactoring and assorted minor optimizations
......
...@@ -402,6 +402,17 @@ public class ErrorCode { ...@@ -402,6 +402,17 @@ public class ErrorCode {
*/ */
public static final int COLUMN_NOT_FOUND_1 = 42122; public static final int COLUMN_NOT_FOUND_1 = 42122;
/**
* The error with code <code>42123</code> is thrown when
* identical expressions should be used, but different
* exceptions were found.
* Example:
* <pre>
* SELECT MODE(A ORDER BY B) FROM TEST;
* </pre>
*/
public static final int IDENTICAL_EXPRESSIONS_SHOULD_BE_USED = 42131;
// 0A: feature not supported // 0A: feature not supported
// HZ: remote database access // HZ: remote database access
...@@ -2068,6 +2079,7 @@ public class ErrorCode { ...@@ -2068,6 +2079,7 @@ public class ErrorCode {
case INDEX_NOT_FOUND_1: return "42S12"; case INDEX_NOT_FOUND_1: return "42S12";
case DUPLICATE_COLUMN_NAME_1: return "42S21"; case DUPLICATE_COLUMN_NAME_1: return "42S21";
case COLUMN_NOT_FOUND_1: return "42S22"; case COLUMN_NOT_FOUND_1: return "42S22";
case IDENTICAL_EXPRESSIONS_SHOULD_BE_USED: return "42S31";
// 0A: feature not supported // 0A: feature not supported
......
...@@ -145,8 +145,6 @@ import org.h2.engine.SysProperties; ...@@ -145,8 +145,6 @@ import org.h2.engine.SysProperties;
import org.h2.engine.User; import org.h2.engine.User;
import org.h2.engine.UserAggregate; import org.h2.engine.UserAggregate;
import org.h2.engine.UserDataType; import org.h2.engine.UserDataType;
import org.h2.expression.Aggregate;
import org.h2.expression.Aggregate.AggregateType;
import org.h2.expression.Alias; import org.h2.expression.Alias;
import org.h2.expression.BinaryOperation; import org.h2.expression.BinaryOperation;
import org.h2.expression.BinaryOperation.OpType; import org.h2.expression.BinaryOperation.OpType;
...@@ -163,7 +161,6 @@ import org.h2.expression.ExpressionColumn; ...@@ -163,7 +161,6 @@ import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionList; import org.h2.expression.ExpressionList;
import org.h2.expression.Function; import org.h2.expression.Function;
import org.h2.expression.FunctionCall; import org.h2.expression.FunctionCall;
import org.h2.expression.JavaAggregate;
import org.h2.expression.JavaFunction; import org.h2.expression.JavaFunction;
import org.h2.expression.Parameter; import org.h2.expression.Parameter;
import org.h2.expression.Rownum; import org.h2.expression.Rownum;
...@@ -174,6 +171,9 @@ import org.h2.expression.UnaryOperation; ...@@ -174,6 +171,9 @@ import org.h2.expression.UnaryOperation;
import org.h2.expression.ValueExpression; import org.h2.expression.ValueExpression;
import org.h2.expression.Variable; import org.h2.expression.Variable;
import org.h2.expression.Wildcard; import org.h2.expression.Wildcard;
import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.JavaAggregate;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
...@@ -1209,12 +1209,7 @@ public class Parser { ...@@ -1209,12 +1209,7 @@ public class Parser {
} }
private int parseSortType() { private int parseSortType() {
int sortType = 0; int sortType = parseSimpleSortType();
if (readIf("ASC")) {
// ignore
} else if (readIf("DESC")) {
sortType = SortOrder.DESCENDING;
}
if (readIf("NULLS")) { if (readIf("NULLS")) {
if (readIf("FIRST")) { if (readIf("FIRST")) {
sortType |= SortOrder.NULLS_FIRST; sortType |= SortOrder.NULLS_FIRST;
...@@ -1226,6 +1221,13 @@ public class Parser { ...@@ -1226,6 +1221,13 @@ public class Parser {
return sortType; return sortType;
} }
private int parseSimpleSortType() {
if (!readIf("ASC") && readIf("DESC")) {
return SortOrder.DESCENDING;
}
return SortOrder.ASCENDING;
}
private String[] parseColumnList() { private String[] parseColumnList() {
ArrayList<String> columns = Utils.newSmallArrayList(); ArrayList<String> columns = Utils.newSmallArrayList();
do { do {
...@@ -2916,40 +2918,35 @@ public class Parser { ...@@ -2916,40 +2918,35 @@ public class Parser {
} }
currentSelect.setGroupQuery(); currentSelect.setGroupQuery();
Aggregate r; Aggregate r;
if (aggregateType == AggregateType.COUNT) { switch (aggregateType) {
case COUNT:
if (readIf(ASTERISK)) { if (readIf(ASTERISK)) {
r = new Aggregate(AggregateType.COUNT_ALL, null, currentSelect, r = new Aggregate(AggregateType.COUNT_ALL, null, currentSelect, false);
false);
} else { } else {
boolean distinct = readIf(DISTINCT); boolean distinct = readIf(DISTINCT);
Expression on = readExpression(); Expression on = readExpression();
if (on instanceof Wildcard && !distinct) { if (on instanceof Wildcard && !distinct) {
// PostgreSQL compatibility: count(t.*) // PostgreSQL compatibility: count(t.*)
r = new Aggregate(AggregateType.COUNT_ALL, null, currentSelect, r = new Aggregate(AggregateType.COUNT_ALL, null, currentSelect, false);
false);
} else { } else {
r = new Aggregate(AggregateType.COUNT, on, currentSelect, r = new Aggregate(AggregateType.COUNT, on, currentSelect, distinct);
distinct);
} }
} }
} else if (aggregateType == AggregateType.GROUP_CONCAT) { break;
case GROUP_CONCAT: {
boolean distinct = readIf(DISTINCT); boolean distinct = readIf(DISTINCT);
if (equalsToken("GROUP_CONCAT", aggregateName)) { if (equalsToken("GROUP_CONCAT", aggregateName)) {
r = new Aggregate(AggregateType.GROUP_CONCAT, r = new Aggregate(AggregateType.GROUP_CONCAT, readExpression(), currentSelect, distinct);
readExpression(), currentSelect, distinct);
if (readIf(ORDER)) { if (readIf(ORDER)) {
read("BY"); read("BY");
r.setOrderByList(parseSimpleOrderList()); r.setOrderByList(parseSimpleOrderList());
} }
if (readIf("SEPARATOR")) { if (readIf("SEPARATOR")) {
r.setGroupConcatSeparator(readExpression()); r.setGroupConcatSeparator(readExpression());
} }
} else if (equalsToken("STRING_AGG", aggregateName)) { } else if (equalsToken("STRING_AGG", aggregateName)) {
// PostgreSQL compatibility: string_agg(expression, delimiter) // PostgreSQL compatibility: string_agg(expression, delimiter)
r = new Aggregate(AggregateType.GROUP_CONCAT, r = new Aggregate(AggregateType.GROUP_CONCAT, readExpression(), currentSelect, distinct);
readExpression(), currentSelect, distinct);
read(COMMA); read(COMMA);
r.setGroupConcatSeparator(readExpression()); r.setGroupConcatSeparator(readExpression());
if (readIf(ORDER)) { if (readIf(ORDER)) {
...@@ -2959,19 +2956,47 @@ public class Parser { ...@@ -2959,19 +2956,47 @@ public class Parser {
} else { } else {
r = null; r = null;
} }
} else if (aggregateType == AggregateType.ARRAY_AGG) { break;
}
case ARRAY_AGG: {
boolean distinct = readIf(DISTINCT); boolean distinct = readIf(DISTINCT);
r = new Aggregate(AggregateType.ARRAY_AGG, readExpression(), currentSelect, distinct);
r = new Aggregate(AggregateType.ARRAY_AGG,
readExpression(), currentSelect, distinct);
if (readIf(ORDER)) { if (readIf(ORDER)) {
read("BY"); read("BY");
r.setOrderByList(parseSimpleOrderList()); r.setOrderByList(parseSimpleOrderList());
} }
break;
}
case MODE: {
if (readIf(CLOSE_PAREN)) {
read("WITHIN");
read(GROUP);
read(OPEN_PAREN);
read(ORDER);
read("BY");
Expression expr = readExpression();
r = new Aggregate(AggregateType.MODE, expr, currentSelect, false);
setModeAggOrder(r, expr);
} else { } else {
Expression expr = readExpression();
r = new Aggregate(aggregateType, expr, currentSelect, false);
if (readIf(ORDER)) {
read("BY");
Expression expr2 = readExpression();
String sql = expr.getSQL(), sql2 = expr2.getSQL();
if (!sql.equals(sql2)) {
throw DbException.getSyntaxError(ErrorCode.IDENTICAL_EXPRESSIONS_SHOULD_BE_USED, sqlCommand,
lastParseIndex, sql, sql2);
}
setModeAggOrder(r, expr);
}
}
break;
}
default:
boolean distinct = readIf(DISTINCT); boolean distinct = readIf(DISTINCT);
r = new Aggregate(aggregateType, readExpression(), currentSelect, r = new Aggregate(aggregateType, readExpression(), currentSelect, distinct);
distinct); break;
} }
read(CLOSE_PAREN); read(CLOSE_PAREN);
if (r != null) { if (r != null) {
...@@ -2980,6 +3005,15 @@ public class Parser { ...@@ -2980,6 +3005,15 @@ public class Parser {
return r; return r;
} }
private void setModeAggOrder(Aggregate r, Expression expr) {
ArrayList<SelectOrderBy> orderList = new ArrayList<>(1);
SelectOrderBy order = new SelectOrderBy();
order.expression = expr;
order.sortType = parseSimpleSortType();
orderList.add(order);
r.setOrderByList(orderList);
}
private ArrayList<SelectOrderBy> parseSimpleOrderList() { private ArrayList<SelectOrderBy> parseSimpleOrderList() {
ArrayList<SelectOrderBy> orderList = Utils.newSmallArrayList(); ArrayList<SelectOrderBy> orderList = Utils.newSmallArrayList();
do { do {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -13,6 +13,9 @@ import org.h2.api.ErrorCode; ...@@ -13,6 +13,9 @@ 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.Session; import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -129,10 +132,16 @@ public class Aggregate extends Expression { ...@@ -129,10 +132,16 @@ public class Aggregate extends Expression {
* The aggregate type for MEDIAN(expression). * The aggregate type for MEDIAN(expression).
*/ */
MEDIAN, MEDIAN,
/** /**
* The aggregate type for ARRAY_AGG(expression). * The aggregate type for ARRAY_AGG(expression).
*/ */
ARRAY_AGG ARRAY_AGG,
/**
* The aggregate type for MODE(expression).
*/
MODE,
} }
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64); private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64);
...@@ -200,6 +209,9 @@ public class Aggregate extends Expression { ...@@ -200,6 +209,9 @@ public class Aggregate extends Expression {
addAggregate("BIT_AND", AggregateType.BIT_AND); addAggregate("BIT_AND", AggregateType.BIT_AND);
addAggregate("MEDIAN", AggregateType.MEDIAN); addAggregate("MEDIAN", AggregateType.MEDIAN);
addAggregate("ARRAY_AGG", AggregateType.ARRAY_AGG); addAggregate("ARRAY_AGG", AggregateType.ARRAY_AGG);
addAggregate("MODE", AggregateType.MODE);
// Oracle compatibility
addAggregate("STATS_MODE", AggregateType.MODE);
} }
private static void addAggregate(String name, AggregateType type) { private static void addAggregate(String name, AggregateType type) {
...@@ -372,7 +384,8 @@ public class Aggregate extends Expression { ...@@ -372,7 +384,8 @@ public class Aggregate extends Expression {
data = AggregateData.create(type); data = AggregateData.create(type);
select.setCurrentGroupExprData(this, data); select.setCurrentGroupExprData(this, data);
} }
if (type == AggregateType.GROUP_CONCAT) { switch (type) {
case GROUP_CONCAT: {
Value[] array = ((AggregateDataCollecting) data).getArray(); Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) { if (array == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
...@@ -399,7 +412,8 @@ public class Aggregate extends Expression { ...@@ -399,7 +412,8 @@ public class Aggregate extends Expression {
buff.append(s); buff.append(s);
} }
return ValueString.get(buff.toString()); return ValueString.get(buff.toString());
} else if (type == AggregateType.ARRAY_AGG) { }
case ARRAY_AGG: {
Value[] array = ((AggregateDataCollecting) data).getArray(); Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) { if (array == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
...@@ -414,8 +428,16 @@ public class Aggregate extends Expression { ...@@ -414,8 +428,16 @@ public class Aggregate extends Expression {
} }
return ValueArray.get(array); return ValueArray.get(array);
} }
case MODE:
if (orderByList != null) {
return ((AggregateDataMode) data).getOrderedValue(session.getDatabase(), dataType,
(orderByList.get(0).sortType & SortOrder.DESCENDING) != 0);
}
//$FALL-THROUGH$
default:
return data.getValue(session.getDatabase(), dataType, distinct); return data.getValue(session.getDatabase(), dataType, distinct);
} }
}
@Override @Override
public int getType() { public int getType() {
...@@ -503,6 +525,7 @@ public class Aggregate extends Expression { ...@@ -503,6 +525,7 @@ public class Aggregate extends Expression {
case MIN: case MIN:
case MAX: case MAX:
case MEDIAN: case MEDIAN:
case MODE:
break; break;
case STDDEV_POP: case STDDEV_POP:
case STDDEV_SAMP: case STDDEV_SAMP:
...@@ -673,6 +696,9 @@ public class Aggregate extends Expression { ...@@ -673,6 +696,9 @@ public class Aggregate extends Expression {
break; break;
case ARRAY_AGG: case ARRAY_AGG:
return getSQLArrayAggregate(); return getSQLArrayAggregate();
case MODE:
text = "MODE";
break;
default: default:
throw DbException.throwInternalError("type=" + type); throw DbException.throwInternalError("type=" + type);
} }
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.expression.Aggregate.AggregateType; import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -35,6 +35,8 @@ abstract class AggregateData { ...@@ -35,6 +35,8 @@ abstract class AggregateData {
return new AggregateDataHistogram(); return new AggregateDataHistogram();
case MEDIAN: case MEDIAN:
return new AggregateDataMedian(); return new AggregateDataMedian();
case MODE:
return new AggregateDataMode();
default: default:
return new AggregateDataDefault(aggregateType); return new AggregateDataDefault(aggregateType);
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.util.ValueHashMap; import org.h2.util.ValueHashMap;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.message.DbException; import org.h2.message.DbException;
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.expression.Aggregate.AggregateType; import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.util.ValueHashMap; import org.h2.util.ValueHashMap;
import org.h2.value.DataType; import org.h2.value.DataType;
......
...@@ -3,11 +3,11 @@ ...@@ -3,11 +3,11 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map; import java.util.Map.Entry;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Mode; import org.h2.engine.Mode;
...@@ -21,37 +21,35 @@ import org.h2.value.ValueLong; ...@@ -21,37 +21,35 @@ import org.h2.value.ValueLong;
* Data stored while calculating a HISTOGRAM aggregate. * Data stored while calculating a HISTOGRAM aggregate.
*/ */
class AggregateDataHistogram extends AggregateData { class AggregateDataHistogram extends AggregateData {
private long count;
private ValueHashMap<AggregateDataHistogram> distinctValues; private ValueHashMap<LongDataCounter> distinctValues;
@Override @Override
void add(Database database, int dataType, boolean distinct, Value v) { void add(Database database, int dataType, boolean distinct, Value v) {
if (distinctValues == null) { if (distinctValues == null) {
distinctValues = ValueHashMap.newInstance(); distinctValues = ValueHashMap.newInstance();
} }
AggregateDataHistogram a = distinctValues.get(v); LongDataCounter a = distinctValues.get(v);
if (a == null) { if (a == null) {
if (distinctValues.size() < Constants.SELECTIVITY_DISTINCT_COUNT) { if (distinctValues.size() >= Constants.SELECTIVITY_DISTINCT_COUNT) {
a = new AggregateDataHistogram(); return;
distinctValues.put(v, a);
} }
a = new LongDataCounter();
distinctValues.put(v, a);
} }
if (a != null) {
a.count++; a.count++;
} }
}
@Override @Override
Value getValue(Database database, int dataType, boolean distinct) { Value getValue(Database database, int dataType, boolean distinct) {
if (distinct) { if (distinctValues == null) {
count = 0; return ValueArray.get(new Value[0]).convertTo(dataType);
groupDistinct(database, dataType);
} }
ValueArray[] values = new ValueArray[distinctValues.size()]; ValueArray[] values = new ValueArray[distinctValues.size()];
int i = 0; int i = 0;
for (Map.Entry<Value,AggregateDataHistogram> entry : distinctValues.entries()) { for (Entry<Value, LongDataCounter> entry : distinctValues.entries()) {
AggregateDataHistogram d = entry.getValue(); LongDataCounter d = entry.getValue();
values[i] = ValueArray.get(new Value[] { entry.getKey(), ValueLong.get(d.count) }); values[i] = ValueArray.get(new Value[] { entry.getKey(), ValueLong.get(distinct ? 1L : d.count) });
i++; i++;
} }
final Mode mode = database.getMode(); final Mode mode = database.getMode();
...@@ -64,18 +62,7 @@ class AggregateDataHistogram extends AggregateData { ...@@ -64,18 +62,7 @@ class AggregateDataHistogram extends AggregateData {
return a1.compareTo(a2, mode, compareMode); return a1.compareTo(a2, mode, compareMode);
} }
}); });
Value v = ValueArray.get(values); return ValueArray.get(values).convertTo(dataType);
return v.convertTo(dataType);
}
private void groupDistinct(Database database, int dataType) {
if (distinctValues == null) {
return;
}
count = 0;
for (Value v : distinctValues.keys()) {
add(database, dataType, false, v);
}
} }
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -14,6 +14,8 @@ import org.h2.engine.Database; ...@@ -14,6 +14,8 @@ import org.h2.engine.Database;
import org.h2.engine.Mode; import org.h2.engine.Mode;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
......
/*
* 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, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) {
return;
}
if (distinctValues == null) {
distinctValues = ValueHashMap.newInstance();
}
LongDataCounter a = distinctValues.get(v);
if (a == null) {
a = new LongDataCounter();
distinctValues.put(v, a);
}
a.count++;
}
@Override
Value getValue(Database database, int dataType, boolean distinct) {
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);
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html). * and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.expression; package org.h2.expression.aggregate;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -13,6 +13,8 @@ import org.h2.command.Parser; ...@@ -13,6 +13,8 @@ import org.h2.command.Parser;
import org.h2.command.dml.Select; import org.h2.command.dml.Select;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.engine.UserAggregate; import org.h2.engine.UserAggregate;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
......
/*
* 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;
/**
* Counter.
*/
final class LongDataCounter {
/**
* The value.
*/
long count;
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, Version 1.0,
and under the Eclipse Public License, Version 1.0
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Aggregate functions.
</p></body></html>
\ No newline at end of file
...@@ -237,6 +237,22 @@ public class DbException extends RuntimeException { ...@@ -237,6 +237,22 @@ public class DbException extends RuntimeException {
null, sql, message)); null, sql, message));
} }
/**
* Create a syntax error exception for a specific error code.
*
* @param errorCode the error code
* @param sql the SQL statement
* @param index the position of the error in the SQL statement
* @param params the list of parameters of the message
* @return the exception
*/
public static DbException getSyntaxError(int errorCode, String sql, int index, String... params) {
sql = StringUtils.addAsterisk(sql, index);
String sqlstate = ErrorCode.getState(errorCode);
String message = translate(sqlstate, params);
return new DbException(getJdbcSQLException(message, sql, sqlstate, errorCode, null, null));
}
/** /**
* Gets a SQL exception meaning this feature is not supported. * Gets a SQL exception meaning this feature is not supported.
* *
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Index {0} nenalezen 42S12=Index {0} nenalezen
42S21=Duplicitní název sloupce {0} 42S21=Duplicitní název sloupce {0}
42S22=Sloupec {0} nenalezen 42S22=Sloupec {0} nenalezen
42S32=Nastavení {0} nenalezeno 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=Příkaz byl zrušen nebo připojení vypršelo 57014=Příkaz byl zrušen nebo připojení vypršelo
90000=Funkce {0} musí vracet výsledek 90000=Funkce {0} musí vracet výsledek
90001=Metoda neumožňuje dotazování. Použijte execute nebo executeQuery namísto executeUpdate 90001=Metoda neumožňuje dotazování. Použijte execute nebo executeQuery namísto executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Index {0} nicht gefunden 42S12=Index {0} nicht gefunden
42S21=Doppelter Feldname {0} 42S21=Doppelter Feldname {0}
42S22=Feld {0} nicht gefunden 42S22=Feld {0} nicht gefunden
42S32=Einstellung {0} nicht gefunden 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=Befehl wurde abgebrochen oder das Session-Timeout ist abgelaufen 57014=Befehl wurde abgebrochen oder das Session-Timeout ist abgelaufen
90000=Funktion {0} muss Zeilen zurückgeben 90000=Funktion {0} muss Zeilen zurückgeben
90001=Methode nicht zulässig für eine Abfrage. Erlaubt sind execute oder executeQuery, nicht jedoch executeUpdate 90001=Methode nicht zulässig für eine Abfrage. Erlaubt sind execute oder executeQuery, nicht jedoch executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Index {0} not found 42S12=Index {0} not found
42S21=Duplicate column name {0} 42S21=Duplicate column name {0}
42S22=Column {0} not found 42S22=Column {0} not found
42S32=Setting {0} not found 42S31=Identical expressions should be used; expected {0}, found {1}
57014=Statement was canceled or the session timed out 57014=Statement was canceled or the session timed out
90000=Function {0} must return a result set 90000=Function {0} must return a result set
90001=Method is not allowed for a query. Use execute or executeQuery instead of executeUpdate 90001=Method is not allowed for a query. Use execute or executeQuery instead of executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Indice {0} no encontrado 42S12=Indice {0} no encontrado
42S21=Nombre de columna Duplicada {0} 42S21=Nombre de columna Duplicada {0}
42S22=Columna {0} no encontrada 42S22=Columna {0} no encontrada
42S32=Setting {0} no encontrado 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=Ls sentencia fue cancelado ó la sesión expiró por tiempo vencido 57014=Ls sentencia fue cancelado ó la sesión expiró por tiempo vencido
90000=Función {0} debe devolver un set de resultados (ResultSet) 90000=Función {0} debe devolver un set de resultados (ResultSet)
90001=Metodo no permitido en un query. Use execute ó executeQuery en lugar de executeUpdate 90001=Metodo no permitido en un query. Use execute ó executeQuery en lugar de executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Index {0} non trouvé 42S12=Index {0} non trouvé
42S21=Duplication du nom de colonnes {0} 42S21=Duplication du nom de colonnes {0}
42S22=Colonne {0} non trouvée 42S22=Colonne {0} non trouvée
42S32=Paramètre {0} non trouvé 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=L'instruction a été annulée ou la session a expiré 57014=L'instruction a été annulée ou la session a expiré
90000=La fonction {0} doit retourner résultat 90000=La fonction {0} doit retourner résultat
90001=Methode non autorisée pour une requête. Utilisez execute ou executeQuery à la place d'executeUpdate 90001=Methode non autorisée pour une requête. Utilisez execute ou executeQuery à la place d'executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=インデックス {0} が見つかりません 42S12=インデックス {0} が見つかりません
42S21=列名 {0} が重複しています 42S21=列名 {0} が重複しています
42S22=列 {0} が見つかりません 42S22=列 {0} が見つかりません
42S32=設定 {0} が見つかりません 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=ステートメントがキャンセルされたか、セッションがタイムアウトしました 57014=ステートメントがキャンセルされたか、セッションがタイムアウトしました
90000=関数 {0} はリザルトセットを返さなければなりません 90000=関数 {0} はリザルトセットを返さなければなりません
90001=メソッドはクエリをサポートしていません。executeUpdateのかわりに、excute、またはexecuteQueryを使用してください 90001=メソッドはクエリをサポートしていません。executeUpdateのかわりに、excute、またはexecuteQueryを使用してください
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Indeks {0} nie istnieje 42S12=Indeks {0} nie istnieje
42S21=Zduplikowana nazwa kolumny {0} 42S21=Zduplikowana nazwa kolumny {0}
42S22=Kolumna {0} nie istnieje 42S22=Kolumna {0} nie istnieje
42S32=Ustawienie {0} nie istnieje 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=Kwerenda została anulowana albo sesja wygasła 57014=Kwerenda została anulowana albo sesja wygasła
90000=Funkcja {0} musi zwrócić dane 90000=Funkcja {0} musi zwrócić dane
90001=Metoda nie jest dozwolona w kwerendzie 90001=Metoda nie jest dozwolona w kwerendzie
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=índice {0} não foi encontrado 42S12=índice {0} não foi encontrado
42S21=Nome duplicado da coluna {0} 42S21=Nome duplicado da coluna {0}
42S22=Coluna {0} não foi encontrada 42S22=Coluna {0} não foi encontrada
42S32=Definição {0} não foi encontrada 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=#Statement was canceled or the session timed out 57014=#Statement was canceled or the session timed out
90000=Função {0} deve retornar algum resultado 90000=Função {0} deve retornar algum resultado
90001=O método não esta hábilitado para consulta. Use o execute ou o executeQuery em vez de executeUpdate 90001=O método não esta hábilitado para consulta. Use o execute ou o executeQuery em vez de executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Индекс {0} не найден 42S12=Индекс {0} не найден
42S21=Повтор имени столбца {0} 42S21=Повтор имени столбца {0}
42S22=Столбец {0} не найден 42S22=Столбец {0} не найден
42S32=Настройка {0} не найдена 42S31=Должны использоваться идентичные выражения; ожидалось {0}, получено {1}
57014=Запрос был отменен или закончилось время ожидания сессии 57014=Запрос был отменен или закончилось время ожидания сессии
90000=Функция {0} должна возвращать набор записей 90000=Функция {0} должна возвращать набор записей
90001=Метод не разрешен для запросов. Используйте execute или executeQuery вместо executeUpdate 90001=Метод не разрешен для запросов. Используйте execute или executeQuery вместо executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=Index {0} nenájdený 42S12=Index {0} nenájdený
42S21=Duplicitné meno stĺpca {0} 42S21=Duplicitné meno stĺpca {0}
42S22=Stĺpec {0} nenájdený 42S22=Stĺpec {0} nenájdený
42S32=Nastavenie {0} nenájdené 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=Príkaz bol zrušený alebo vypršal časový limit sedenia 57014=Príkaz bol zrušený alebo vypršal časový limit sedenia
90000=Funkcia {0} musí vracať výsledok (result set) 90000=Funkcia {0} musí vracať výsledok (result set)
90001=Metóda nie je povolená pre dopyt (query). Použite execute alebo executeQuery namiesto executeUpdate 90001=Metóda nie je povolená pre dopyt (query). Použite execute alebo executeQuery namiesto executeUpdate
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
42S12=找不到索引 {0} 42S12=找不到索引 {0}
42S21=重复的字段: {0} 42S21=重复的字段: {0}
42S22=找不到字段 {0} 42S22=找不到字段 {0}
42S32=找不到设置 {0} 42S31=#Identical expressions should be used; expected {0}, found {1}
57014=语句已取消执行或会话已过期 57014=语句已取消执行或会话已过期
90000={0} 函数必须返回一个结果集 90000={0} 函数必须返回一个结果集
90001=不允许在查询内使用的方法,使用execute 或 executeQuery 代替 executeUpdate 90001=不允许在查询内使用的方法,使用execute 或 executeQuery 代替 executeUpdate
......
...@@ -137,7 +137,7 @@ public class TestScript extends TestDb { ...@@ -137,7 +137,7 @@ public class TestScript extends TestDb {
testScript("other/" + s + ".sql"); testScript("other/" + s + ".sql");
} }
for (String s : new String[] { "avg", "bit-and", "bit-or", "count", for (String s : new String[] { "avg", "bit-and", "bit-or", "count",
"group-concat", "max", "median", "min", "selectivity", "stddev-pop", "group-concat", "max", "median", "min", "mode", "selectivity", "stddev-pop",
"stddev-samp", "sum", "var-pop", "var-samp", "array-agg" }) { "stddev-samp", "sum", "var-pop", "var-samp", "array-agg" }) {
testScript("functions/aggregate/" + s + ".sql"); testScript("functions/aggregate/" + s + ".sql");
} }
......
-- 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
--
CREATE TABLE TEST(V INT);
> ok
SELECT MODE(V) FROM TEST;
>> null
SELECT MODE(DISTINCT V) FROM TEST;
> exception SYNTAX_ERROR_2
INSERT INTO TEST VALUES (NULL);
> update count: 1
SELECT MODE(V) FROM TEST;
>> null
INSERT INTO TEST VALUES (1), (2), (3), (1), (2), (1);
> update count: 6
SELECT MODE(V), MODE(V) FILTER (WHERE (V > 1)), MODE(V) FILTER (WHERE (V < 0)) FROM TEST;
> MODE(V) MODE(V) FILTER (WHERE (V > 1)) MODE(V) FILTER (WHERE (V < 0))
> ------- ------------------------------ ------------------------------
> 1 2 null
> rows: 1
-- Oracle compatibility
SELECT STATS_MODE(V) FROM TEST;
>> 1
INSERT INTO TEST VALUES (2), (3), (3);
> update count: 3
SELECT MODE(V ORDER BY V) FROM TEST;
>> 1
SELECT MODE(V ORDER BY V ASC) FROM TEST;
>> 1
SELECT MODE(V ORDER BY V DESC) FROM TEST;
>> 3
SELECT MODE(V ORDER BY V + 1) FROM TEST;
> exception IDENTICAL_EXPRESSIONS_SHOULD_BE_USED
SELECT MODE() WITHIN GROUP(ORDER BY V) FROM TEST;
>> 1
SELECT MODE() WITHIN GROUP(ORDER BY V ASC) FROM TEST;
>> 1
SELECT MODE() WITHIN GROUP(ORDER BY V DESC) FROM TEST;
>> 3
DROP TABLE TEST;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论