提交 d08cab6c authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add ROW_NUMBER(), RANK(), and DENSE_RANK() window functions

上级 a306265c
......@@ -2533,6 +2533,19 @@ The column list of the resulting table is C1, C2, and so on.
SELECT * FROM (VALUES(1, 'Hello'), (2, 'World')) AS V;
"
"Other Grammar","Window specification","
([PARTITION BY expression [,...])] [ORDER BY order [,...]])
","
A window specification for a window function or aggregate.
Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries.
","
()
(ORDER BY ID)
(PARTITION BY CATEGORY)
(PARTITION BY CATEGORY ORDER BY NAME, ID)
"
"Other Grammar","Term","
value
| columnName
......@@ -3251,7 +3264,8 @@ INTERVAL MINUTE TO SECOND
"
"Functions (Aggregate)","AVG","
AVG ( [ DISTINCT ] { numeric } ) [ FILTER ( WHERE expression ) ]
AVG ( [ DISTINCT ] { numeric } )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The average (mean) value.
If no rows are selected, the result is NULL.
......@@ -3262,7 +3276,8 @@ AVG(X)
"
"Functions (Aggregate)","BIT_AND","
BIT_AND(expression) [ FILTER ( WHERE expression ) ]
BIT_AND(expression)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The bitwise AND of all non-null values.
If no rows are selected, the result is NULL.
......@@ -3272,7 +3287,8 @@ BIT_AND(ID)
"
"Functions (Aggregate)","BIT_OR","
BIT_OR(expression) [ FILTER ( WHERE expression ) ]
BIT_OR(expression)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The bitwise OR of all non-null values.
If no rows are selected, the result is NULL.
......@@ -3282,7 +3298,8 @@ BIT_OR(ID)
"
"Functions (Aggregate)","BOOL_AND","
BOOL_AND(boolean) [ FILTER ( WHERE expression ) ]
BOOL_AND(boolean)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Returns true if all expressions are true.
If no rows are selected, the result is NULL.
......@@ -3292,7 +3309,8 @@ BOOL_AND(ID>10)
"
"Functions (Aggregate)","BOOL_OR","
BOOL_OR(boolean) [ FILTER ( WHERE expression ) ]
BOOL_OR(boolean)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Returns true if any expression is true.
If no rows are selected, the result is NULL.
......@@ -3302,7 +3320,8 @@ BOOL_OR(NAME LIKE 'W%')
"
"Functions (Aggregate)","COUNT","
COUNT( { * | { [ DISTINCT ] expression } } ) [ FILTER ( WHERE expression ) ]
COUNT( { * | { [ DISTINCT ] expression } } )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The count of all row, or of the non-null values.
This method returns a long.
......@@ -3315,7 +3334,8 @@ COUNT(*)
"Functions (Aggregate)","GROUP_CONCAT","
GROUP_CONCAT ( [ DISTINCT ] string
[ ORDER BY { expression [ ASC | DESC ] } [,...] ]
[ SEPARATOR expression ] ) [ FILTER ( WHERE expression ) ]
[ SEPARATOR expression ] )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Concatenates strings with a separator.
The default separator is a ',' (without space).
......@@ -3329,7 +3349,7 @@ GROUP_CONCAT(NAME ORDER BY ID SEPARATOR ', ')
"Functions (Aggregate)","ARRAY_AGG","
ARRAY_AGG ( [ DISTINCT ] string
[ ORDER BY { expression [ ASC | DESC ] } [,...] ] )
[ FILTER ( WHERE expression ) ]
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Aggregate the value into an array.
This method returns an array.
......@@ -3340,7 +3360,8 @@ ARRAY_AGG(NAME ORDER BY ID)
"
"Functions (Aggregate)","MAX","
MAX(value) [ FILTER ( WHERE expression ) ]
MAX(value)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The highest value.
If no rows are selected, the result is NULL.
......@@ -3351,7 +3372,8 @@ MAX(NAME)
"
"Functions (Aggregate)","MIN","
MIN(value) [ FILTER ( WHERE expression ) ]
MIN(value)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The lowest value.
If no rows are selected, the result is NULL.
......@@ -3362,7 +3384,8 @@ MIN(NAME)
"
"Functions (Aggregate)","SUM","
SUM( [ DISTINCT ] { numeric } ) [ FILTER ( WHERE expression ) ]
SUM( [ DISTINCT ] { numeric } )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The sum of all values.
If no rows are selected, the result is NULL.
......@@ -3374,7 +3397,8 @@ SUM(X)
"
"Functions (Aggregate)","SELECTIVITY","
SELECTIVITY(value) [ FILTER ( WHERE expression ) ]
SELECTIVITY(value)
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Estimates the selectivity (0-100) of a value.
The value is defined as (100 * distinctCount / rowCount).
......@@ -3386,7 +3410,8 @@ SELECT SELECTIVITY(FIRSTNAME), SELECTIVITY(NAME) FROM TEST WHERE ROWNUM()<20000
"
"Functions (Aggregate)","STDDEV_POP","
STDDEV_POP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
STDDEV_POP( [ DISTINCT ] numeric )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The population standard deviation.
This method returns a double.
......@@ -3397,7 +3422,8 @@ STDDEV_POP(X)
"
"Functions (Aggregate)","STDDEV_SAMP","
STDDEV_SAMP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
STDDEV_SAMP( [ DISTINCT ] numeric )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The sample standard deviation.
This method returns a double.
......@@ -3408,7 +3434,8 @@ STDDEV(X)
"
"Functions (Aggregate)","VAR_POP","
VAR_POP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
VAR_POP( [ DISTINCT ] numeric )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The population variance (square of the population standard deviation).
This method returns a double.
......@@ -3419,7 +3446,8 @@ VAR_POP(X)
"
"Functions (Aggregate)","VAR_SAMP","
VAR_SAMP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
VAR_SAMP( [ DISTINCT ] numeric )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The sample variance (square of the sample standard deviation).
This method returns a double.
......@@ -3430,7 +3458,8 @@ VAR_SAMP(X)
"
"Functions (Aggregate)","MEDIAN","
MEDIAN( [ DISTINCT ] value ) [ FILTER ( WHERE expression ) ]
MEDIAN( [ DISTINCT ] value )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
The value separating the higher half of a values from the lower half.
Returns the middle value or an interpolated value between two middle values if number of values is even.
......@@ -3445,7 +3474,7 @@ MEDIAN(X)
"Functions (Aggregate)","MODE","
{ MODE( value ) [ ORDER BY expression [ ASC | DESC ] ] }
| { MODE() WITHIN GROUP(ORDER BY expression [ ASC | DESC ]) }
[ FILTER ( WHERE expression ) ]
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Returns the value that occurs with the greatest frequency.
If there are multiple values with the same frequency only one value will be returned.
......@@ -3464,7 +3493,8 @@ MODE() WITHIN GROUP(ORDER BY X)
"
"Functions (Aggregate)","ENVELOPE","
ENVELOPE( value ) [ FILTER ( WHERE expression ) ]
ENVELOPE( value )
[FILTER (WHERE expression)] [OVER windowSpecification]
","
Returns the minimum bounding box that encloses all specified GEOMETRY values.
Only 2D coordinate plane is supported.
......@@ -4931,14 +4961,14 @@ READONLY()
"
"Functions (System)","ROWNUM","
{ ROWNUM() } | { ROW_NUMBER() OVER() }
ROWNUM()
","
Returns the number of the current row.
This method returns a long.
This method returns an integer value.
It is supported for SELECT statements, as well as for DELETE and UPDATE.
The first row has the row number 1, and is calculated before ordering and grouping the result set,
but after evaluating index conditions (even when the index conditions are specified in an outer query).
To get the row number after ordering and grouping, use a subquery.
Use the ROW_NUMBER() OVER () function to get row numbers after grouping or in specified order.
","
SELECT ROWNUM(), * FROM TEST;
SELECT ROWNUM(), * FROM (SELECT * FROM TEST ORDER BY NAME);
......@@ -5036,6 +5066,49 @@ Returns the H2 version as a String.
H2VERSION()
"
"Functions (Window)","ROW_NUMBER","
ROW_NUMBER() OVER windowSpecification
","
Returns the number of the current row starting with 1.
Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries.
","
SELECT ROW_NUMBER() OVER (), * FROM TEST;
SELECT ROW_NUMBER() OVER (ORDER BY ID), * FROM TEST;
SELECT ROW_NUMBER() OVER (PARTITION BY CATEGORY ORDER BY ID), * FROM TEST;
"
"Functions (Window)","RANK","
RANK() OVER windowSpecification
","
Returns the rank of the current row.
The rank of a row is the number of rows that precede this row plus 1.
If two or more rows have the same values in ORDER BY columns, these rows get the same rank from the first row with the same values.
It means that gaps in ranks are possible.
Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries.
","
SELECT RANK() OVER (ORDER BY ID), * FROM TEST;
SELECT RANK() OVER (PARTITION BY CATEGORY ORDER BY ID), * FROM TEST;
"
"Functions (Window)","DENSE_RANK","
DENSE_RANK() OVER windowSpecification
","
Returns the dense rank of the current row.
The rank of a row is the number of groups of rows with the same values in ORDER BY columns that precede group with this row plus 1.
If two or more rows have the same values in ORDER BY columns, these rows get the same rank.
Gaps in ranks are not possible.
Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries.
","
SELECT RANK() OVER (ORDER BY ID), * FROM TEST;
SELECT RANK() OVER (PARTITION BY CATEGORY ORDER BY ID), * FROM TEST;
"
"System Tables","Information Schema","
INFORMATION_SCHEMA
","
......
......@@ -159,6 +159,34 @@ syntax-end -->
</table>
<!-- railroad-end -->
<h3>Window Functions</h3>
<!-- syntax-start
<p class="notranslate">
<c:forEach var="item" items="functionsWindow">
<a href="#${item.link}" >${item.topic}</a><br />
</c:forEach>
</p>
syntax-end -->
<!-- railroad-start -->
<table class="notranslate index">
<tr>
<td class="index">
<c:forEach var="item" items="functionsWindow-0">
<a href="#${item.link}" >${item.topic}</a><br />
</c:forEach>
</td><td class="index">
<c:forEach var="item" items="functionsWindow-1">
<a href="#${item.link}" >${item.topic}</a><br />
</c:forEach>
</td><td class="index">
<c:forEach var="item" items="functionsWindow-2">
<a href="#${item.link}" >${item.topic}</a><br />
</c:forEach>
</td>
</tr>
</table>
<!-- railroad-end -->
<!-- railroad-start -->
<h2>Details</h2>
<p>Click on the header to switch between railroad diagram and BNF.</p>
......@@ -269,6 +297,27 @@ syntax-end -->
<p class="notranslate">${item.example}</p>
</c:forEach>
<h2>Window Functions</h2>
<c:forEach var="item" items="functionsWindow">
<h3 id="${item.link}" class="notranslate" onclick="switchBnf(this)">${item.topic}</h3>
<!-- railroad-start -->
<pre name="bnf" style="display: none">
${item.syntax}
</pre>
<div name="railroad">
${item.railroad}
</div>
<!-- railroad-end -->
<!-- syntax-start
<pre>
${item.syntax}
</pre>
syntax-end -->
<p>${item.text}</p>
<p>Example:</p>
<p class="notranslate">${item.example}</p>
</c:forEach>
<!--[if lte IE 7]><script language="javascript">switchBnf(null);</script><![endif]-->
<!-- [close] { --></div></td></tr></table><!-- } --><!-- analytics --></body></html>
......
......@@ -175,6 +175,8 @@ import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.WindowFunction;
import org.h2.expression.aggregate.WindowFunction.WindowFunctionType;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.result.SortOrder;
......@@ -3032,13 +3034,15 @@ public class Parser {
}
private void readFilterAndOver(AbstractAggregate aggregate) {
if (readIf("FILTER")) {
boolean isAggregate = aggregate.isAggregate();
if (isAggregate && readIf("FILTER")) {
read(OPEN_PAREN);
read(WHERE);
Expression filterCondition = readExpression();
read(CLOSE_PAREN);
aggregate.setFilterCondition(filterCondition);
}
Window over = null;
if (readIf("OVER")) {
read(OPEN_PAREN);
ArrayList<Expression> partitionBy = null;
......@@ -3054,10 +3058,15 @@ public class Parser {
if (readIf(ORDER)) {
read("BY");
orderBy = parseSimpleOrderList();
} else if (!isAggregate) {
orderBy = new ArrayList<>(0);
}
read(CLOSE_PAREN);
aggregate.setOverCondition(new Window(partitionBy, orderBy));
over = new Window(partitionBy, orderBy);
aggregate.setOverCondition(over);
currentSelect.setWindowQuery();
} else if (!isAggregate) {
throw getSyntaxError();
} else {
currentSelect.setGroupQuery();
}
......@@ -3088,6 +3097,10 @@ public class Parser {
}
Function function = Function.getFunction(database, name);
if (function == null) {
WindowFunction windowFunction = readWindowFunction(name);
if (windowFunction != null) {
return windowFunction;
}
UserAggregate aggregate = database.findAggregate(name);
if (aggregate != null) {
return readJavaAggregate(aggregate);
......@@ -3233,16 +3246,6 @@ public class Parser {
tf.setColumns(columns);
break;
}
case Function.ROW_NUMBER:
read(CLOSE_PAREN);
read("OVER");
read(OPEN_PAREN);
read(CLOSE_PAREN);
if (currentSelect == null && currentPrepared == null) {
throw getSyntaxError();
}
return new Rownum(currentSelect == null ? currentPrepared
: currentSelect);
default:
if (!readIf(CLOSE_PAREN)) {
int i = 0;
......@@ -3255,6 +3258,24 @@ public class Parser {
return function;
}
private WindowFunction readWindowFunction(String name) {
if (!database.getSettings().databaseToUpper) {
// if not yet converted to uppercase, do it now
name = StringUtils.toUpperEnglish(name);
}
WindowFunctionType type = WindowFunctionType.get(name);
if (type == null) {
return null;
}
if (currentSelect == null) {
throw getSyntaxError();
}
read(CLOSE_PAREN);
WindowFunction function = new WindowFunction(type, currentSelect);
readFilterAndOver(function);
return function;
}
private Expression readFunctionWithoutParameters(String name) {
if (database.isAllowBuiltinAliasOverride()) {
FunctionAlias functionAlias = database.getSchema(session.getCurrentSchemaName()).findFunction(name);
......
......@@ -146,8 +146,6 @@ public class Function extends Expression implements FunctionCall {
*/
public static final int H2VERSION = 231;
public static final int ROW_NUMBER = 300;
protected static final int VAR_ARGS = -1;
private static final long PRECISION_UNKNOWN = -1;
......@@ -470,9 +468,6 @@ public class Function extends Expression implements FunctionCall {
addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT,
VAR_ARGS, Value.RESULT_SET);
// pseudo function
addFunctionWithNull("ROW_NUMBER", ROW_NUMBER, 0, Value.LONG);
// ON DUPLICATE KEY VALUES function
addFunction("VALUES", VALUES, 1, Value.NULL, false, true, false);
}
......
......@@ -25,7 +25,7 @@ import org.h2.value.ValueArray;
import org.h2.value.ValueInt;
/**
* A base class for aggregates.
* A base class for aggregates and window functions.
*/
public abstract class AbstractAggregate extends Expression {
......@@ -65,7 +65,11 @@ public abstract class AbstractAggregate extends Expression {
* FILTER condition
*/
public void setFilterCondition(Expression filterCondition) {
this.filterCondition = filterCondition;
if (isAggregate()) {
this.filterCondition = filterCondition;
} else {
throw DbException.getUnsupportedException("Window function");
}
}
/**
......@@ -78,6 +82,23 @@ public abstract class AbstractAggregate extends Expression {
this.over = over;
}
/**
* Checks whether this expression is an aggregate function.
*
* @return true if this is an aggregate function (including aggregates with
* OVER clause), false if this is a window function
*/
public abstract boolean isAggregate();
/**
* Returns the sort order for OVER clause.
*
* @return the sort order for OVER clause
*/
SortOrder getOverOrderBySort() {
return overOrderBySort;
}
@Override
public void mapColumns(ColumnResolver resolver, int level) {
if (filterCondition != null) {
......
......@@ -251,6 +251,11 @@ public class Aggregate extends AbstractAggregate {
return AGGREGATES.get(name);
}
@Override
public boolean isAggregate() {
return true;
}
/**
* Set the order for ARRAY_AGG() or GROUP_CONCAT() aggregate.
*
......
......@@ -40,6 +40,11 @@ public class JavaAggregate extends AbstractAggregate {
this.args = args;
}
@Override
public boolean isAggregate() {
return true;
}
@Override
public int getCost() {
int cost = 5;
......
......@@ -33,7 +33,7 @@ public final class Window {
* ORDER BY clause, or null
*/
static void appendOrderBy(StringBuilder builder, ArrayList<SelectOrderBy> orderBy) {
if (orderBy != null) {
if (orderBy != null && !orderBy.isEmpty()) {
builder.append(" ORDER BY ");
for (int i = 0; i < orderBy.size(); i++) {
SelectOrderBy o = orderBy.get(i);
......
/*
* 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.command.dml.Select;
import org.h2.engine.Session;
import org.h2.expression.ExpressionVisitor;
import org.h2.message.DbException;
import org.h2.value.Value;
import org.h2.value.ValueInt;
/**
* A window function.
*/
public class WindowFunction extends AbstractAggregate {
/**
* A type of a window function.
*/
public enum WindowFunctionType {
/**
* The type for ROW_NUMBER() window function.
*/
ROW_NUMBER,
/**
* The type for RANK() window function.
*/
RANK,
/**
* The type for DENSE_RANK() window function.
*/
DENSE_RANK,
;
/**
* Returns the type of window function with the specified name, or null.
*
* @param name
* name of a window function
* @return the type of window function, or null.
*/
public static WindowFunctionType get(String name) {
switch (name) {
case "ROW_NUMBER":
return WindowFunctionType.ROW_NUMBER;
case "RANK":
return RANK;
case "DENSE_RANK":
return WindowFunctionType.DENSE_RANK;
default:
return null;
}
}
}
private static class RowNumberData {
int number;
RowNumberData() {
}
}
private static final class RankData extends RowNumberData {
Value[] previousRow;
int previousNumber;
RankData() {
}
}
private WindowFunctionType type;
/**
* Creates new instance of a window function.
*
* @param type
* the type
* @param select
* the select statement
*/
public WindowFunction(WindowFunctionType type, Select select) {
super(select, false);
this.type = type;
}
@Override
public boolean isAggregate() {
return false;
}
@Override
protected void updateAggregate(Session session, Object aggregateData) {
switch (type) {
case ROW_NUMBER:
((RowNumberData) aggregateData).number++;
break;
case RANK:
case DENSE_RANK: {
RankData data = (RankData) aggregateData;
data.number++;
data.previousNumber++;
break;
}
default:
throw DbException.throwInternalError("type=" + type);
}
}
@Override
protected void updateGroupAggregates(Session session, int stage) {
// Nothing to do
}
@Override
protected int getNumExpressions() {
return 0;
}
@Override
protected void rememberExpressions(Session session, Value[] array) {
// Nothing to do
}
@Override
protected void updateFromExpressions(Session session, Object aggregateData, Value[] array) {
switch (type) {
case ROW_NUMBER:
((RowNumberData) aggregateData).number++;
break;
case RANK:
case DENSE_RANK: {
RankData data = (RankData) aggregateData;
data.number++;
Value[] previous = data.previousRow;
if (previous == null) {
data.previousNumber++;
} else {
if (getOverOrderBySort().compare(previous, array) != 0) {
if (type == WindowFunctionType.RANK) {
data.previousNumber = data.number;
} else /* DENSE_RANK */ {
data.previousNumber++;
}
}
}
data.previousRow = array;
break;
}
default:
throw DbException.throwInternalError("type=" + type);
}
}
@Override
protected Object createAggregateData() {
switch (type) {
case ROW_NUMBER:
return new RowNumberData();
case RANK:
case DENSE_RANK:
return new RankData();
default:
throw DbException.throwInternalError("type=" + type);
}
}
@Override
protected Value getAggregatedValue(Session session, Object aggregateData) {
switch (type) {
case ROW_NUMBER:
return ValueInt.get(((RowNumberData) aggregateData).number);
case RANK:
case DENSE_RANK:
return ValueInt.get(((RankData) aggregateData).previousNumber);
default:
throw DbException.throwInternalError("type=" + type);
}
}
@Override
public int getType() {
return Value.INT;
}
@Override
public int getScale() {
return 0;
}
@Override
public long getPrecision() {
return ValueInt.PRECISION;
}
@Override
public int getDisplaySize() {
return ValueInt.DISPLAY_SIZE;
}
@Override
public String getSQL() {
String text;
switch (type) {
case ROW_NUMBER:
text = "ROW_NUMBER";
break;
case RANK:
text = "RANK";
break;
case DENSE_RANK:
text = "DENSE_RANK";
break;
default:
throw DbException.throwInternalError("type=" + type);
}
StringBuilder builder = new StringBuilder().append(text).append("()");
return appendTailConditions(builder).toString();
}
@Override
public boolean isEverything(ExpressionVisitor visitor) {
if (visitor.getType() == ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL) {
return false;
}
return true;
}
@Override
public int getCost() {
int cost = 1;
return cost;
}
}
......@@ -9,6 +9,6 @@ Initial Developer: H2 Group
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Aggregate functions.
Aggregate and window functions.
</p></body></html>
\ No newline at end of file
......@@ -136,9 +136,9 @@ public class TestScript extends TestDb {
for (String s : new String[] { "help" }) {
testScript("other/" + s + ".sql");
}
for (String s : new String[] { "avg", "bit-and", "bit-or", "count", "envelope",
for (String s : new String[] { "array-agg", "avg", "bit-and", "bit-or", "count", "envelope",
"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" }) {
testScript("functions/aggregate/" + s + ".sql");
}
for (String s : new String[] { "abs", "acos", "asin", "atan", "atan2",
......@@ -179,6 +179,9 @@ public class TestScript extends TestDb {
"parsedatetime", "quarter", "second", "truncate", "week", "year", "date_trunc" }) {
testScript("functions/timeanddate/" + s + ".sql");
}
for (String s : new String[] { "row_number" }) {
testScript("functions/window/" + s + ".sql");
}
deleteDb("script");
System.out.flush();
......
......@@ -7,7 +7,7 @@
create table test as (select char(x) as str from system_range(48,90));
> ok
select row_number() over () as rnum, str from test where str = 'A';
select rownum() as rnum, str from test where str = 'A';
> RNUM STR
> ---- ---
> 1 A
......
-- 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 (ID INT PRIMARY KEY, CATEGORY INT, VALUE INT);
> ok
INSERT INTO TEST VALUES
(1, 1, 11),
(2, 1, 12),
(3, 1, 13),
(4, 2, 21),
(5, 2, 22),
(6, 3, 31),
(7, 3, 32),
(8, 3, 33);
> update count: 8
SELECT *,
ROW_NUMBER() OVER () RN, RANK() OVER () RK, DENSE_RANK() OVER () DR,
ROW_NUMBER() OVER (ORDER BY ID) RNO, RANK() OVER (ORDER BY ID) RKO, DENSE_RANK() OVER (ORDER BY ID) DRO
FROM TEST;
> ID CATEGORY VALUE RN RK DR RNO RKO DRO
> -- -------- ----- -- -- -- --- --- ---
> 1 1 11 1 1 1 1 1 1
> 2 1 12 2 1 1 2 2 2
> 3 1 13 3 1 1 3 3 3
> 4 2 21 4 1 1 4 4 4
> 5 2 22 5 1 1 5 5 5
> 6 3 31 6 1 1 6 6 6
> 7 3 32 7 1 1 7 7 7
> 8 3 33 8 1 1 8 8 8
> rows (ordered): 8
SELECT *,
ROW_NUMBER() OVER (ORDER BY CATEGORY) RN, RANK() OVER (ORDER BY CATEGORY) RK, DENSE_RANK() OVER (ORDER BY CATEGORY) DR
FROM TEST;
> ID CATEGORY VALUE RN RK DR
> -- -------- ----- -- -- --
> 1 1 11 1 1 1
> 2 1 12 2 1 1
> 3 1 13 3 1 1
> 4 2 21 4 4 2
> 5 2 22 5 4 2
> 6 3 31 6 6 3
> 7 3 32 7 6 3
> 8 3 33 8 6 3
> rows (ordered): 8
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CATEGORY ORDER BY ID) RN,
RANK() OVER (PARTITION BY CATEGORY ORDER BY ID) RK,
DENSE_RANK() OVER (PARTITION BY CATEGORY ORDER BY ID) DR
FROM TEST;
> ID CATEGORY VALUE RN RK DR
> -- -------- ----- -- -- --
> 1 1 11 1 1 1
> 2 1 12 2 2 2
> 3 1 13 3 3 3
> 4 2 21 1 1 1
> 5 2 22 2 2 2
> 6 3 31 1 1 1
> 7 3 32 2 2 2
> 8 3 33 3 3 3
> rows (ordered): 8
SELECT
ROW_NUMBER() OVER () RN, RANK() OVER () RK, DENSE_RANK() OVER () DR
FROM TEST GROUP BY CATEGORY;
> RN RK DR
> -- -- --
> 1 1 1
> 2 1 1
> 3 1 1
> rows: 3
DROP TABLE TEST;
> ok
......@@ -281,21 +281,21 @@ create table test(id int primary key, name varchar(255), row_number int);
insert into test values(1, 'hello', 10), (2, 'world', 20);
> update count: 2
select row_number() over(), id, name from test order by id;
select rownum(), id, name from test order by id;
> ROWNUM() ID NAME
> -------- -- -----
> 1 1 hello
> 2 2 world
> rows (ordered): 2
select row_number() over(), id, name from test order by name;
select rownum(), id, name from test order by name;
> ROWNUM() ID NAME
> -------- -- -----
> 1 1 hello
> 2 2 world
> rows (ordered): 2
select row_number() over(), id, name from test order by name desc;
select rownum(), id, name from test order by name desc;
> ROWNUM() ID NAME
> -------- -- -----
> 2 2 world
......
......@@ -90,6 +90,8 @@ public class GenerateDoc {
help + "= 'Functions (Time and Date)' ORDER BY ID", true, false);
map("functionsSystem",
help + "= 'Functions (System)' ORDER BY ID", true, false);
map("functionsWindow",
help + "= 'Functions (Window)' ORDER BY ID", true, false);
map("dataTypes",
help + "LIKE 'Data Types%' ORDER BY SECTION, ID", true, true);
map("intervalDataTypes",
......
......@@ -796,3 +796,4 @@ interior envelopes multilinestring multipoint packed exterior normalization awkw
xym normalizes coord setz xyzm geometrycollection multipolygon mixup rings polygons rejection finite
pointzm pointz pointm dimensionality redefine forum measures
mpg casted pzm mls constrained subtypes complains
ranks rno dro rko precede
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论