提交 0c17e600 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add window frame exclusion clause implementation

上级 32d3ca3f
......@@ -2535,17 +2535,10 @@ SELECT * FROM (VALUES(1, 'Hello'), (2, 'World')) AS V;
"Other Grammar","Window specification","
([PARTITION BY expression [,...]] [ORDER BY order [,...]]
[RANGE BETWEEN {
UNBOUNDED PRECEDING AND CURRENT ROW
|CURRENT ROW AND UNBOUNDED FOLLOWING
|UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
}])
[windowFrame])
","
A window specification for a window function or aggregate.
Window frame clause (RANGE) is currently supported only in aggregates and
FIRST_VALUE(), LAST_VALUE(), and NTH_VALUE() window functions.
Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries.
","
......@@ -2553,6 +2546,21 @@ They also may require a lot of memory for large queries.
(ORDER BY ID)
(PARTITION BY CATEGORY)
(PARTITION BY CATEGORY ORDER BY NAME, ID)
(ORDER BY Y RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE TIES)
"
"Other Grammar","Window frame","
[RANGE BETWEEN {
UNBOUNDED PRECEDING AND CURRENT ROW
|CURRENT ROW AND UNBOUNDED FOLLOWING
|UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
}]
[EXCLUDE {CURRENT ROW|GROUP|TIES|NO OTHERS}]
","
A window frame clause.
Is currently supported only in aggregates and FIRST_VALUE(), LAST_VALUE(), and NTH_VALUE() window functions.
","
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP
"
"Other Grammar","Term","
......
......@@ -177,6 +177,7 @@ import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.WindowFrame;
import org.h2.expression.aggregate.WindowFrame.SimpleExtent;
import org.h2.expression.aggregate.WindowFrame.WindowFrameExclusion;
import org.h2.expression.aggregate.WindowFunction;
import org.h2.expression.aggregate.WindowFunction.WindowFunctionType;
import org.h2.index.Index;
......@@ -3073,7 +3074,8 @@ public class Parser {
frame = readWindowFrame();
break;
default:
frame = new WindowFrame(SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW);
frame = new WindowFrame(SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW,
WindowFrameExclusion.EXCLUDE_NO_OTHERS);
}
} else {
frame = readWindowFrame();
......@@ -3091,6 +3093,7 @@ public class Parser {
private WindowFrame readWindowFrame() {
SimpleExtent extent;
WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS;
if (readIf("RANGE")) {
read("BETWEEN");
if (readIf("UNBOUNDED")) {
......@@ -3112,10 +3115,23 @@ public class Parser {
read("FOLLOWING");
extent = SimpleExtent.RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING;
}
if (readIf("EXCLUDE")) {
if (readIf("CURRENT")) {
read("ROW");
exclusion = WindowFrameExclusion.EXCLUDE_CURRENT_ROW;
} else if (readIf(GROUP)) {
exclusion = WindowFrameExclusion.EXCLUDE_GROUP;
} else if (readIf("TIES")) {
exclusion = WindowFrameExclusion.EXCLUDE_TIES;
} else {
read("NO");
read("OTHERS");
}
}
} else {
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
}
return new WindowFrame(extent);
return new WindowFrame(extent, exclusion);
}
private AggregateType getAggregateType(String name) {
......
......@@ -8,6 +8,7 @@ package org.h2.expression.aggregate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import org.h2.api.ErrorCode;
import org.h2.command.dml.Select;
......@@ -441,12 +442,12 @@ public abstract class AbstractAggregate extends Expression {
result.put(row[rowIdColumn].getInt(), value);
}
} else {
// TODO optimize unordered aggregates
int size = ordered.size();
for (int i = 0; i < size; i++) {
Object aggregateData = createAggregateData();
for (int j = i; j < size; j++) {
updateFromExpressions(session, aggregateData, ordered.get(j));
for (Iterator<Value[]> iter = frame.iterator(ordered, getOverOrderBySort(), i, false); iter
.hasNext();) {
updateFromExpressions(session, aggregateData, iter.next());
}
result.put(ordered.get(i)[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData));
}
......
......@@ -36,7 +36,10 @@ public final class Window {
*/
static void appendOrderBy(StringBuilder builder, ArrayList<SelectOrderBy> orderBy) {
if (orderBy != null && !orderBy.isEmpty()) {
builder.append(" ORDER BY ");
if (builder.charAt(builder.length() - 1) != '(') {
builder.append(' ');
}
builder.append("ORDER BY ");
for (int i = 0; i < orderBy.size(); i++) {
SelectOrderBy o = orderBy.get(i);
if (i > 0) {
......@@ -187,10 +190,10 @@ public final class Window {
builder.append(StringUtils.unEnclose(partitionBy.get(i).getSQL()));
}
}
appendOrderBy(builder, orderBy);
if (!frame.isDefault()) {
builder.append(' ').append(frame.getSQL());
}
appendOrderBy(builder, orderBy);
return builder.append(')').toString();
}
......
......@@ -321,11 +321,11 @@ public class WindowFunction extends AbstractAggregate {
Value v;
switch (type) {
case FIRST_VALUE: {
v = getNthValue(frame.iterator(ordered, i), 0, ignoreNulls);
v = getNthValue(frame.iterator(ordered, getOverOrderBySort(), i, false), 0, ignoreNulls);
break;
}
case LAST_VALUE:
v = getNthValue(frame.reverseIterator(ordered, i), 0, ignoreNulls);
v = getNthValue(frame.iterator(ordered, getOverOrderBySort(), i, true), 0, ignoreNulls);
break;
case NTH_VALUE: {
int n = row[1].getInt();
......@@ -333,7 +333,7 @@ public class WindowFunction extends AbstractAggregate {
throw DbException.getInvalidValueException("nth row", n);
}
n--;
Iterator<Value[]> iter = fromLast ? frame.reverseIterator(ordered, i) : frame.iterator(ordered, i);
Iterator<Value[]> iter = frame.iterator(ordered, getOverOrderBySort(), i, fromLast);
v = getNthValue(iter, n, ignoreNulls);
break;
}
......
......@@ -233,8 +233,8 @@ SELECT
> rows: 6
SELECT ARRAY_AGG(SUM(ID)) OVER(ORDER /**/ BY ID) FROM TEST GROUP BY ID;
> ARRAY_AGG(SUM(ID)) OVER ( ORDER BY ID)
> --------------------------------------
> ARRAY_AGG(SUM(ID)) OVER (ORDER BY ID)
> -------------------------------------
> (1)
> (1, 2)
> (1, 2, 3)
......@@ -245,3 +245,33 @@ SELECT ARRAY_AGG(SUM(ID)) OVER(ORDER /**/ BY ID) FROM TEST GROUP BY ID;
DROP TABLE TEST;
> ok
CREATE TABLE TEST(ID INT, G INT);
> ok
INSERT INTO TEST VALUES
(1, 1),
(2, 2),
(3, 2),
(4, 2),
(5, 3);
> update count: 5
SELECT
ARRAY_AGG(ID) OVER (ORDER BY G RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) D,
ARRAY_AGG(ID) OVER (ORDER BY G RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) R,
ARRAY_AGG(ID) OVER (ORDER BY G RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP) G,
ARRAY_AGG(ID) OVER (ORDER BY G RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES) T,
ARRAY_AGG(ID) OVER (ORDER BY G RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS) N
FROM TEST;
> D R G T N
> --------------- ------------ ------------ --------------- ---------------
> (1, 2, 3, 4, 5) (2, 3, 4, 5) (2, 3, 4, 5) (1, 2, 3, 4, 5) (1, 2, 3, 4, 5)
> (1, 2, 3, 4, 5) (1, 3, 4, 5) (1, 5) (1, 2, 5) (1, 2, 3, 4, 5)
> (1, 2, 3, 4, 5) (1, 2, 4, 5) (1, 5) (1, 3, 5) (1, 2, 3, 4, 5)
> (1, 2, 3, 4, 5) (1, 2, 3, 5) (1, 5) (1, 4, 5) (1, 2, 3, 4, 5)
> (1, 2, 3, 4, 5) (1, 2, 3, 4) (1, 2, 3, 4) (1, 2, 3, 4, 5) (1, 2, 3, 4, 5)
> rows (ordered): 5
DROP TABLE TEST;
> ok
......@@ -135,5 +135,16 @@ SELECT *,
> 13 4 null 41 null null
> rows (ordered): 13
SELECT ID, CATEGORY,
NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) C,
NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
FROM TEST FETCH FIRST 3 ROWS ONLY;
> ID CATEGORY C NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
> -- -------- ---- ---------------------------------------------------------------------------------------------------------------------
> 1 1 null null
> 2 1 1 null
> 3 1 1 1
> rows (ordered): 3
DROP TABLE TEST;
> ok
......@@ -796,4 +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 cume reopens preceding unbounded rightly
ranks rno dro rko precede cume reopens preceding unbounded rightly itr
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论