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

Merge pull request #1701 from katzyn/percentile

Add PERCENTILE_CONT and PERCENTILE_DISC inverse distribution functions
......@@ -3674,6 +3674,38 @@ Aggregates are only allowed in select statements.
VAR_SAMP(X)
"
"Functions (Aggregate)","PERCENTILE_CONT","
PERCENTILE_CONT(numeric) WITHIN GROUP (ORDER BY value [ASC|DESC])
[FILTER (WHERE expression)] [OVER windowNameOrSpecification]
","
Return percentile of values from the group with interpolation.
Interpolation is only supported for numeric, date-time, and interval data types.
Argument must be between 0 and 1 inclusive.
Argument must be the same for all rows in the same group.
If argument is NULL, the result is NULL.
NULL values are ignored in the calculation.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY V)
"
"Functions (Aggregate)","PERCENTILE_DISC","
PERCENTILE_DISC(numeric) WITHIN GROUP (ORDER BY value [ASC|DESC])
[FILTER (WHERE expression)] [OVER windowNameOrSpecification]
","
Return percentile of values from the group.
Interpolation is not performed.
Argument must be between 0 and 1 inclusive.
Argument must be the same for all rows in the same group.
If argument is NULL, the result is NULL.
NULL values are ignored in the calculation.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY V)
"
"Functions (Aggregate)","MEDIAN","
MEDIAN( [ DISTINCT|ALL ] value )
[FILTER (WHERE expression)] [OVER windowNameOrSpecification]
......@@ -3690,7 +3722,7 @@ MEDIAN(X)
"Functions (Aggregate)","MODE","
{ MODE( value ) [ ORDER BY expression [ ASC | DESC ] ] }
| { MODE() WITHIN GROUP(ORDER BY expression [ ASC | DESC ]) }
| { MODE() WITHIN GROUP (ORDER BY expression [ ASC | DESC ]) }
[FILTER (WHERE expression)] [OVER windowNameOrSpecification]
","
Returns the value that occurs with the greatest frequency.
......@@ -3706,7 +3738,7 @@ Aggregates are only allowed in select statements.
","
MODE(X)
MODE(X ORDER BY X)
MODE() WITHIN GROUP(ORDER BY X)
MODE() WITHIN GROUP (ORDER BY X)
"
"Functions (Aggregate)","ENVELOPE","
......
......@@ -21,6 +21,20 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>PR #1701: Add PERCENTILE_CONT and PERCENTILE_DISC inverse distribution functions
</li>
<li>Issues #1297, #1697: Failure on concurrent session closure
</li>
<li>Issue #1297: removeOldTempIndexes on PageStore causes NullPointerException
</li>
<li>Issue #1354: TestCrashAPI: another NPE
</li>
<li>PR #1695: Reduce memory for TestMVTempResult to 64m
</li>
<li>Issue #1691: Append mode causes OOME in MVPlainTempResult
</li>
<li>PR #1692: Use MVTempResult unconditionally
</li>
<li>Issue #1689: Use separate constants for data types in Data, ValueDataType, and Transfer
</li>
<li>PR #1687: MVMap minor cleanup
......
......@@ -414,7 +414,7 @@ public class ErrorCode {
/**
* The error with code <code>42131</code> is thrown when
* identical expressions should be used, but different
* exceptions were found.
* expressions were found.
* Example:
* <pre>
* SELECT MODE(A ORDER BY B) FROM TEST;
......
......@@ -177,7 +177,7 @@ import org.h2.expression.Variable;
import org.h2.expression.Wildcard;
import org.h2.expression.aggregate.AbstractAggregate;
import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.AggregateType;
import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.analysis.DataAnalysisOperation;
import org.h2.expression.analysis.Window;
......@@ -3055,18 +3055,9 @@ public class Parser {
break;
case GROUP_CONCAT: {
boolean distinct = readDistinctAgg();
if (equalsToken("GROUP_CONCAT", aggregateName)) {
r = new Aggregate(AggregateType.GROUP_CONCAT, readExpression(), currentSelect, distinct);
if (readIf(ORDER)) {
read("BY");
r.setOrderByList(parseSimpleOrderList());
}
if (readIf("SEPARATOR")) {
r.setGroupConcatSeparator(readExpression());
}
} else if (equalsToken("STRING_AGG", aggregateName)) {
r = new Aggregate(AggregateType.GROUP_CONCAT, readExpression(), currentSelect, distinct);
if (equalsToken("STRING_AGG", aggregateName)) {
// PostgreSQL compatibility: string_agg(expression, delimiter)
r = new Aggregate(AggregateType.GROUP_CONCAT, readExpression(), currentSelect, distinct);
read(COMMA);
r.setGroupConcatSeparator(readExpression());
if (readIf(ORDER)) {
......@@ -3074,7 +3065,13 @@ public class Parser {
r.setOrderByList(parseSimpleOrderList());
}
} else {
r = null;
if (readIf(ORDER)) {
read("BY");
r.setOrderByList(parseSimpleOrderList());
}
if (readIf("SEPARATOR")) {
r.setGroupConcatSeparator(readExpression());
}
}
break;
}
......@@ -3087,19 +3084,19 @@ public class Parser {
}
break;
}
case PERCENTILE_CONT:
case PERCENTILE_DISC: {
Expression num = readExpression();
read(CLOSE_PAREN);
r = readWithinGroup(aggregateType, num);
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);
r = readWithinGroup(AggregateType.MODE, null);
} else {
Expression expr = readExpression();
r = new Aggregate(aggregateType, expr, currentSelect, false);
r = new Aggregate(aggregateType, null, currentSelect, false);
if (readIf(ORDER)) {
read("BY");
Expression expr2 = readExpression();
......@@ -3108,7 +3105,9 @@ public class Parser {
throw DbException.getSyntaxError(ErrorCode.IDENTICAL_EXPRESSIONS_SHOULD_BE_USED, sqlCommand,
lastParseIndex, sql, sql2);
}
setModeAggOrder(r, expr);
readAggregateOrder(r, expr, true);
} else {
readAggregateOrder(r, expr, false);
}
}
break;
......@@ -3119,17 +3118,30 @@ public class Parser {
break;
}
read(CLOSE_PAREN);
if (r != null) {
readFilterAndOver(r);
}
readFilterAndOver(r);
return r;
}
private void setModeAggOrder(Aggregate r, Expression expr) {
private Aggregate readWithinGroup(AggregateType aggregateType, Expression argument) {
Aggregate r;
read("WITHIN");
read(GROUP);
read(OPEN_PAREN);
read(ORDER);
read("BY");
Expression expr = readExpression();
r = new Aggregate(aggregateType, argument, currentSelect, false);
readAggregateOrder(r, expr, true);
return r;
}
private void readAggregateOrder(Aggregate r, Expression expr, boolean parseSortType) {
ArrayList<SelectOrderBy> orderList = new ArrayList<>(1);
SelectOrderBy order = new SelectOrderBy();
order.expression = expr;
order.sortType = parseSimpleSortType();
if (parseSortType) {
order.sortType = parseSimpleSortType();
}
orderList.add(order);
r.setOrderByList(orderList);
}
......
......@@ -48,113 +48,6 @@ import org.h2.value.ValueString;
*/
public class Aggregate extends AbstractAggregate {
public enum AggregateType {
/**
* The aggregate type for COUNT(*).
*/
COUNT_ALL,
/**
* The aggregate type for COUNT(expression).
*/
COUNT,
/**
* The aggregate type for GROUP_CONCAT(...).
*/
GROUP_CONCAT,
/**
* The aggregate type for SUM(expression).
*/
SUM,
/**
* The aggregate type for MIN(expression).
*/
MIN,
/**
* The aggregate type for MAX(expression).
*/
MAX,
/**
* The aggregate type for AVG(expression).
*/
AVG,
/**
* The aggregate type for STDDEV_POP(expression).
*/
STDDEV_POP,
/**
* The aggregate type for STDDEV_SAMP(expression).
*/
STDDEV_SAMP,
/**
* The aggregate type for VAR_POP(expression).
*/
VAR_POP,
/**
* The aggregate type for VAR_SAMP(expression).
*/
VAR_SAMP,
/**
* The aggregate type for ANY(expression).
*/
ANY,
/**
* The aggregate type for EVERY(expression).
*/
EVERY,
/**
* The aggregate type for BOOL_OR(expression).
*/
BIT_OR,
/**
* The aggregate type for BOOL_AND(expression).
*/
BIT_AND,
/**
* The aggregate type for SELECTIVITY(expression).
*/
SELECTIVITY,
/**
* The aggregate type for HISTOGRAM(expression).
*/
HISTOGRAM,
/**
* The aggregate type for MEDIAN(expression).
*/
MEDIAN,
/**
* The aggregate type for ARRAY_AGG(expression).
*/
ARRAY_AGG,
/**
* The aggregate type for MODE(expression).
*/
MODE,
/**
* The aggregate type for ENVELOPE(expression).
*/
ENVELOPE,
}
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64);
private final AggregateType aggregateType;
......@@ -218,6 +111,8 @@ public class Aggregate extends AbstractAggregate {
addAggregate("HISTOGRAM", AggregateType.HISTOGRAM);
addAggregate("BIT_OR", AggregateType.BIT_OR);
addAggregate("BIT_AND", AggregateType.BIT_AND);
addAggregate("PERCENTILE_CONT", AggregateType.PERCENTILE_CONT);
addAggregate("PERCENTILE_DISC", AggregateType.PERCENTILE_DISC);
addAggregate("MEDIAN", AggregateType.MEDIAN);
addAggregate("ARRAY_AGG", AggregateType.ARRAY_AGG);
addAggregate("MODE", AggregateType.MODE);
......@@ -294,14 +189,27 @@ public class Aggregate extends AbstractAggregate {
}
private void updateData(Session session, AggregateData data, Value v, Value[] remembered) {
if (aggregateType == AggregateType.GROUP_CONCAT) {
switch (aggregateType) {
case GROUP_CONCAT:
if (v != ValueNull.INSTANCE) {
v = updateCollecting(session, v.convertTo(Value.STRING), remembered);
}
} else if (aggregateType == AggregateType.ARRAY_AGG) {
break;
case ARRAY_AGG:
if (v != ValueNull.INSTANCE) {
v = updateCollecting(session, v, remembered);
}
break;
case PERCENTILE_CONT:
case PERCENTILE_DISC:
((AggregateDataCollecting) data).setSharedArgument(v);
v = remembered != null ? remembered[1] : orderByList.get(0).expression.getValue(session);
break;
case MODE:
v = remembered != null ? remembered[0] : orderByList.get(0).expression.getValue(session);
break;
default:
// Use argument as is
}
data.add(session.getDatabase(), v);
}
......@@ -408,8 +316,23 @@ public class Aggregate extends AbstractAggregate {
}
return v;
}
case PERCENTILE_CONT:
case PERCENTILE_DISC: {
Value v = on.getValue(session);
if (v == ValueNull.INSTANCE) {
return ValueNull.INSTANCE;
}
double arg = v.getDouble();
if (arg >= 0d && arg <= 1d) {
return Percentile.getFromIndex(session, orderByList.get(0).expression, type.getValueType(),
orderByList, arg, aggregateType == AggregateType.PERCENTILE_CONT);
} else {
throw DbException.getInvalidValueException(aggregateType == AggregateType.PERCENTILE_CONT ?
"PERCENTILE_CONT argument" : "PERCENTILE_DISC argument", arg);
}
}
case MEDIAN:
return AggregateMedian.medianFromIndex(session, on, type.getValueType());
return Percentile.getFromIndex(session, on, type.getValueType(), orderByList, 0.5d, true);
case ENVELOPE:
return ((MVSpatialIndex) AggregateDataEnvelope.getGeometryColumnIndex(on)).getBounds(session);
default:
......@@ -468,12 +391,32 @@ public class Aggregate extends AbstractAggregate {
}
return ValueArray.get(array);
}
case PERCENTILE_CONT:
case PERCENTILE_DISC: {
AggregateDataCollecting collectingData = (AggregateDataCollecting) data;
Value[] array = collectingData.getArray();
if (array == null) {
return ValueNull.INSTANCE;
}
Value v = collectingData.getSharedArgument();
if (v == ValueNull.INSTANCE) {
return ValueNull.INSTANCE;
}
double arg = v.getDouble();
if (arg >= 0d && arg <= 1d) {
return Percentile.getValue(session.getDatabase(), array, type.getValueType(), orderByList, arg,
aggregateType == AggregateType.PERCENTILE_CONT);
} else {
throw DbException.getInvalidValueException(aggregateType == AggregateType.PERCENTILE_CONT ?
"PERCENTILE_CONT argument" : "PERCENTILE_DISC argument", arg);
}
}
case MEDIAN: {
Value[] array = ((AggregateDataCollecting) data).getArray();
if (array == null) {
return ValueNull.INSTANCE;
}
return AggregateMedian.median(session.getDatabase(), array, type.getValueType());
return Percentile.getValue(session.getDatabase(), array, type.getValueType(), orderByList, 0.5d, true);
}
case MODE:
return getMode(session, data);
......@@ -647,7 +590,11 @@ public class Aggregate extends AbstractAggregate {
case MIN:
case MAX:
case MEDIAN:
break;
case PERCENTILE_CONT:
case PERCENTILE_DISC:
case MODE:
type = orderByList.get(0).expression.getType();
break;
case STDDEV_POP:
case STDDEV_SAMP:
......@@ -772,6 +719,12 @@ public class Aggregate extends AbstractAggregate {
case BIT_OR:
text = "BIT_OR";
break;
case PERCENTILE_CONT:
text = "PERCENTILE_CONT";
break;
case PERCENTILE_DISC:
text = "PERCENTILE_DISC";
break;
case MEDIAN:
text = "MEDIAN";
break;
......@@ -792,13 +745,20 @@ public class Aggregate extends AbstractAggregate {
on.getSQL(builder).append(')');
} else {
builder.append('(');
if (on instanceof Subquery) {
on.getSQL(builder);
} else {
on.getUnenclosedSQL(builder);
if (on != null) {
if (on instanceof Subquery) {
on.getSQL(builder);
} else {
on.getUnenclosedSQL(builder);
}
}
builder.append(')');
}
if (orderByList != null) {
builder.append(" WITHIN GROUP (");
Window.appendOrderBy(builder, orderByList);
builder.append(')');
}
return appendTailConditions(builder);
}
......@@ -836,11 +796,14 @@ public class Aggregate extends AbstractAggregate {
case MAX:
Index index = getMinMaxColumnIndex();
return index != null;
case PERCENTILE_CONT:
case PERCENTILE_DISC:
return on.isConstant() && Percentile.getColumnIndex(orderByList.get(0).expression) != null;
case MEDIAN:
if (distinct) {
return false;
}
return AggregateMedian.getMedianColumnIndex(on) != null;
return Percentile.getColumnIndex(on) != null;
case ENVELOPE:
return AggregateDataEnvelope.getGeometryColumnIndex(on) != null;
default:
......
......@@ -7,7 +7,6 @@ package org.h2.expression.aggregate;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.message.DbException;
import org.h2.value.Value;
......@@ -35,6 +34,8 @@ abstract class AggregateData {
break;
case GROUP_CONCAT:
case ARRAY_AGG:
case PERCENTILE_CONT:
case PERCENTILE_DISC:
case MEDIAN:
break;
case MIN:
......
......@@ -11,7 +11,9 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.TreeSet;
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.message.DbException;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -31,6 +33,8 @@ class AggregateDataCollecting extends AggregateData implements Iterable<Value> {
Collection<Value> values;
private Value shared;
/**
* Creates new instance of data for collecting aggregates.
*
......@@ -84,4 +88,27 @@ class AggregateDataCollecting extends AggregateData implements Iterable<Value> {
return values != null ? values.iterator() : Collections.<Value>emptyIterator();
}
/**
* Sets value of a shared argument.
*
* @param shared the shared value
*/
void setSharedArgument(Value shared) {
if (this.shared == null) {
this.shared = shared;
} else if (!this.shared.equals(shared)) {
throw DbException.get(ErrorCode.INVALID_VALUE_2, "Inverse distribution function argument",
this.shared.getTraceSQL() + "<>" + shared.getTraceSQL());
}
}
/**
* Returns value of a shared argument.
*
* @return value of a shared argument
*/
Value getSharedArgument() {
return shared;
}
}
......@@ -6,7 +6,6 @@
package org.h2.expression.aggregate;
import org.h2.engine.Database;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.message.DbException;
import org.h2.value.DataType;
import org.h2.value.Value;
......
/*
* Copyright 2004-2019 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;
/**
* The type of an aggregate function.
*/
public enum AggregateType {
/**
* The aggregate type for COUNT(*).
*/
COUNT_ALL,
/**
* The aggregate type for COUNT(expression).
*/
COUNT,
/**
* The aggregate type for GROUP_CONCAT(...).
*/
GROUP_CONCAT,
/**
* The aggregate type for SUM(expression).
*/
SUM,
/**
* The aggregate type for MIN(expression).
*/
MIN,
/**
* The aggregate type for MAX(expression).
*/
MAX,
/**
* The aggregate type for AVG(expression).
*/
AVG,
/**
* The aggregate type for STDDEV_POP(expression).
*/
STDDEV_POP,
/**
* The aggregate type for STDDEV_SAMP(expression).
*/
STDDEV_SAMP,
/**
* The aggregate type for VAR_POP(expression).
*/
VAR_POP,
/**
* The aggregate type for VAR_SAMP(expression).
*/
VAR_SAMP,
/**
* The aggregate type for ANY(expression).
*/
ANY,
/**
* The aggregate type for EVERY(expression).
*/
EVERY,
/**
* The aggregate type for BOOL_OR(expression).
*/
BIT_OR,
/**
* The aggregate type for BOOL_AND(expression).
*/
BIT_AND,
/**
* The aggregate type for SELECTIVITY(expression).
*/
SELECTIVITY,
/**
* The aggregate type for HISTOGRAM(expression).
*/
HISTOGRAM,
/**
* The aggregate type for PERCENTILE_CONT(expression).
*/
PERCENTILE_CONT,
/**
* The aggregate type for PERCENTILE_DISC(expression).
*/
PERCENTILE_DISC,
/**
* The aggregate type for MEDIAN(expression).
*/
MEDIAN,
/**
* The aggregate type for ARRAY_AGG(expression).
*/
ARRAY_AGG,
/**
* The aggregate type for MODE(expression).
*/
MODE,
/**
* The aggregate type for ENVELOPE(expression).
*/
ENVELOPE,
}
......@@ -15,7 +15,7 @@ import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.AggregateType;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.table.Column;
......
......@@ -841,16 +841,16 @@ public class MVMap<K, V> extends AbstractMap<K, V>
/**
* Use the new root page from now on.
* @param expectedRootRefrence expected current root reference
* @param expectedRootReference expected current root reference
* @param newRootPage the new root page
* @param attemptUpdateCounter how many attempt (including current)
* were made to update root
* @return new RootReference or null if update failed
*/
protected final boolean updateRoot(RootReference expectedRootRefrence, Page newRootPage,
int attemptUpdateCounter) {
protected final boolean updateRoot(RootReference expectedRootReference, Page newRootPage, int attemptUpdateCounter)
{
RootReference currentRoot = flushAndGetRoot();
return currentRoot == expectedRootRefrence &&
return currentRoot == expectedRootReference &&
!currentRoot.lockedForUpdate &&
root.compareAndSet(currentRoot,
new RootReference(currentRoot, newRootPage, attemptUpdateCounter));
......
......@@ -506,7 +506,8 @@ public class MVStore implements AutoCloseable {
if (id >= 0) {
map = openMap(id, builder);
assert builder.getKeyType() == null || map.getKeyType().getClass().equals(builder.getKeyType().getClass());
assert builder.getValueType() == null || map.getValueType().getClass().equals(builder.getValueType().getClass());
assert builder.getValueType() == null || map.getValueType().getClass().equals(builder.getValueType()
.getClass());
} else {
HashMap<String, Object> c = new HashMap<>();
id = lastMapId.incrementAndGet();
......
......@@ -435,13 +435,13 @@ public abstract class Value extends VersionedValue {
return 43_000;
case GEOMETRY:
return 44_000;
case ENUM:
return 45_000;
case ARRAY:
return 50_000;
case ROW:
return 50_500;
case RESULT_SET:
return 51_000;
case ENUM:
case RESULT_SET:
return 52_000;
default:
if (JdbcUtils.customDataTypesHandler != null) {
......@@ -1337,18 +1337,19 @@ public abstract class Value extends VersionedValue {
return ValueArray.get(a);
}
private ValueRow convertToRow() {
private Value convertToRow() {
Value[] a;
switch (getValueType()) {
case ARRAY:
a = ((ValueArray) this).getList();
break;
case BLOB:
case CLOB:
case RESULT_SET:
a = new Value[] { ValueString.get(getString()) };
break;
default:
if (getValueType() == RESULT_SET) {
ResultInterface result = ((ValueResultSet) this).getResult();
if (result.hasNext()) {
a = result.currentRow();
if (result.hasNext()) {
throw DbException.get(ErrorCode.SCALAR_SUBQUERY_CONTAINS_MORE_THAN_ONE_ROW);
}
} else {
return ValueNull.INSTANCE;
}
} else {
a = new Value[] { this };
}
return ValueRow.get(a);
......
......@@ -165,7 +165,7 @@ public class TestScript extends TestDb {
testScript("other/" + s + ".sql");
}
for (String s : new String[] { "any", "array-agg", "avg", "bit-and", "bit-or", "count", "envelope",
"every", "group-concat", "histogram", "max", "median", "min", "mode", "selectivity", "stddev-pop",
"every", "group-concat", "histogram", "max", "min", "mode", "percentile", "selectivity", "stddev-pop",
"stddev-samp", "sum", "var-pop", "var-samp" }) {
testScript("functions/aggregate/" + s + ".sql");
}
......
......@@ -84,5 +84,13 @@ SELECT ARRAY[1, NULL] IN (SELECT A FROM TEST);
SELECT ROW (ARRAY[1, NULL]) IN (SELECT A FROM TEST);
>> null
-- Compatibility with H2 1.4.197 and older
SELECT A FROM TEST WHERE A = (1, 2);
>> [1, 2]
-- Compatibility with H2 1.4.197 and older
INSERT INTO TEST VALUES ((1, 3));
> update count: 1
DROP TABLE TEST;
> ok
......@@ -21,10 +21,16 @@ SELECT MODE(V) FROM TEST;
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
SELECT MODE(V), MODE() WITHIN GROUP (ORDER BY V DESC) FROM TEST;
> MODE() WITHIN GROUP (ORDER BY V) MODE() WITHIN GROUP (ORDER BY V DESC)
> -------------------------------- -------------------------------------
> 1 1
> rows: 1
SELECT MODE(V) FILTER (WHERE (V > 1)), MODE(V) FILTER (WHERE (V < 0)) FROM TEST;
> MODE() WITHIN GROUP (ORDER BY V) FILTER (WHERE (V > 1)) MODE() WITHIN GROUP (ORDER BY V) FILTER (WHERE (V < 0))
> ------------------------------------------------------- -------------------------------------------------------
> 2 null
> rows: 1
-- Oracle compatibility
......@@ -46,15 +52,35 @@ SELECT MODE(V ORDER BY V DESC) FROM TEST;
SELECT MODE(V ORDER BY V + 1) FROM TEST;
> exception IDENTICAL_EXPRESSIONS_SHOULD_BE_USED
SELECT MODE() WITHIN GROUP(ORDER BY V) FROM TEST;
SELECT MODE() WITHIN GROUP (ORDER BY V) FROM TEST;
>> 1
SELECT MODE() WITHIN GROUP(ORDER BY V ASC) FROM TEST;
SELECT MODE() WITHIN GROUP (ORDER BY V ASC) FROM TEST;
>> 1
SELECT MODE() WITHIN GROUP(ORDER BY V DESC) FROM TEST;
SELECT MODE() WITHIN GROUP (ORDER BY V DESC) FROM TEST;
>> 3
SELECT
MODE() WITHIN GROUP (ORDER BY V) OVER () MA,
MODE() WITHIN GROUP (ORDER BY V DESC) OVER () MD,
MODE() WITHIN GROUP (ORDER BY V) OVER (ORDER BY V) MWA,
MODE() WITHIN GROUP (ORDER BY V DESC) OVER (ORDER BY V) MWD,
V FROM TEST;
> MA MD MWA MWD V
> -- -- ---- ---- ----
> 1 3 1 1 1
> 1 3 1 1 1
> 1 3 1 1 1
> 1 3 1 2 2
> 1 3 1 2 2
> 1 3 1 2 2
> 1 3 1 3 3
> 1 3 1 3 3
> 1 3 1 3 3
> 1 3 null null null
> rows: 10
DROP TABLE TEST;
> ok
......
......@@ -806,4 +806,4 @@ econd irst bcef ordinality nord unnest
analyst occupation distributive josaph aor engineer sajeewa isuru randil kevin doctor businessman artist ashan
corrupts splitted disruption unintentional octets preconditions predicates subq objectweb insn opcodes
preserves masking holder unboxing avert iae transformed subtle reevaluate exclusions subclause ftbl rgr
presorted inclusion contexts aax
presorted inclusion contexts aax mwd percentile cont interpolate mwa
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论