提交 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; ...@@ -2535,17 +2535,10 @@ SELECT * FROM (VALUES(1, 'Hello'), (2, 'World')) AS V;
"Other Grammar","Window specification"," "Other Grammar","Window specification","
([PARTITION BY expression [,...]] [ORDER BY order [,...]] ([PARTITION BY expression [,...]] [ORDER BY order [,...]]
[RANGE BETWEEN { [windowFrame])
UNBOUNDED PRECEDING AND CURRENT ROW
|CURRENT ROW AND UNBOUNDED FOLLOWING
|UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
}])
"," ","
A window specification for a window function or aggregate. 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. Window functions are currently experimental in H2 and should be used with caution.
They also may require a lot of memory for large queries. 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. ...@@ -2553,6 +2546,21 @@ They also may require a lot of memory for large queries.
(ORDER BY ID) (ORDER BY ID)
(PARTITION BY CATEGORY) (PARTITION BY CATEGORY)
(PARTITION BY CATEGORY ORDER BY NAME, ID) (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"," "Other Grammar","Term","
......
...@@ -177,6 +177,7 @@ import org.h2.expression.aggregate.JavaAggregate; ...@@ -177,6 +177,7 @@ import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window; import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.WindowFrame; import org.h2.expression.aggregate.WindowFrame;
import org.h2.expression.aggregate.WindowFrame.SimpleExtent; 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;
import org.h2.expression.aggregate.WindowFunction.WindowFunctionType; import org.h2.expression.aggregate.WindowFunction.WindowFunctionType;
import org.h2.index.Index; import org.h2.index.Index;
...@@ -3073,7 +3074,8 @@ public class Parser { ...@@ -3073,7 +3074,8 @@ public class Parser {
frame = readWindowFrame(); frame = readWindowFrame();
break; break;
default: 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 { } else {
frame = readWindowFrame(); frame = readWindowFrame();
...@@ -3091,6 +3093,7 @@ public class Parser { ...@@ -3091,6 +3093,7 @@ public class Parser {
private WindowFrame readWindowFrame() { private WindowFrame readWindowFrame() {
SimpleExtent extent; SimpleExtent extent;
WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS;
if (readIf("RANGE")) { if (readIf("RANGE")) {
read("BETWEEN"); read("BETWEEN");
if (readIf("UNBOUNDED")) { if (readIf("UNBOUNDED")) {
...@@ -3112,10 +3115,23 @@ public class Parser { ...@@ -3112,10 +3115,23 @@ public class Parser {
read("FOLLOWING"); read("FOLLOWING");
extent = SimpleExtent.RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_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 { } else {
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW; extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
} }
return new WindowFrame(extent); return new WindowFrame(extent, exclusion);
} }
private AggregateType getAggregateType(String name) { private AggregateType getAggregateType(String name) {
......
...@@ -8,6 +8,7 @@ package org.h2.expression.aggregate; ...@@ -8,6 +8,7 @@ package org.h2.expression.aggregate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.dml.Select; import org.h2.command.dml.Select;
...@@ -441,12 +442,12 @@ public abstract class AbstractAggregate extends Expression { ...@@ -441,12 +442,12 @@ public abstract class AbstractAggregate extends Expression {
result.put(row[rowIdColumn].getInt(), value); result.put(row[rowIdColumn].getInt(), value);
} }
} else { } else {
// TODO optimize unordered aggregates
int size = ordered.size(); int size = ordered.size();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Object aggregateData = createAggregateData(); Object aggregateData = createAggregateData();
for (int j = i; j < size; j++) { for (Iterator<Value[]> iter = frame.iterator(ordered, getOverOrderBySort(), i, false); iter
updateFromExpressions(session, aggregateData, ordered.get(j)); .hasNext();) {
updateFromExpressions(session, aggregateData, iter.next());
} }
result.put(ordered.get(i)[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData)); result.put(ordered.get(i)[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData));
} }
......
...@@ -36,7 +36,10 @@ public final class Window { ...@@ -36,7 +36,10 @@ public final class Window {
*/ */
static void appendOrderBy(StringBuilder builder, ArrayList<SelectOrderBy> orderBy) { static void appendOrderBy(StringBuilder builder, ArrayList<SelectOrderBy> orderBy) {
if (orderBy != null && !orderBy.isEmpty()) { 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++) { for (int i = 0; i < orderBy.size(); i++) {
SelectOrderBy o = orderBy.get(i); SelectOrderBy o = orderBy.get(i);
if (i > 0) { if (i > 0) {
...@@ -187,10 +190,10 @@ public final class Window { ...@@ -187,10 +190,10 @@ public final class Window {
builder.append(StringUtils.unEnclose(partitionBy.get(i).getSQL())); builder.append(StringUtils.unEnclose(partitionBy.get(i).getSQL()));
} }
} }
appendOrderBy(builder, orderBy);
if (!frame.isDefault()) { if (!frame.isDefault()) {
builder.append(' ').append(frame.getSQL()); builder.append(' ').append(frame.getSQL());
} }
appendOrderBy(builder, orderBy);
return builder.append(')').toString(); return builder.append(')').toString();
} }
......
...@@ -6,10 +6,13 @@ ...@@ -6,10 +6,13 @@
package org.h2.expression.aggregate; package org.h2.expression.aggregate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.SortOrder;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -59,16 +62,190 @@ public final class WindowFrame { ...@@ -59,16 +62,190 @@ public final class WindowFrame {
} }
/**
* Window frame exclusion clause.
*/
public enum WindowFrameExclusion {
/**
* EXCLUDE CURRENT ROW exclusion clause.
*/
EXCLUDE_CURRENT_ROW("EXCLUDE CURRENT ROW"),
/**
* EXCLUDE GROUP exclusion clause.
*/
EXCLUDE_GROUP("EXCLUDE GROUP"),
/**
* EXCLUDE TIES exclusion clause.
*/
EXCLUDE_TIES("EXCLUDE TIES"),
/**
* EXCLUDE NO OTHERS exclusion clause.
*/
EXCLUDE_NO_OTHERS("EXCLUDE NO OTHERS"),
;
private final String sql;
private WindowFrameExclusion(String sql) {
this.sql = sql;
}
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see org.h2.expression.Expression#getSQL()
*/
public String getSQL() {
return sql;
}
}
private abstract class Itr implements Iterator<Value[]> {
final ArrayList<Value[]> orderedRows;
Itr(ArrayList<Value[]> orderedRows) {
this.orderedRows = orderedRows;
}
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
}
private final class PlainItr extends Itr {
private final int endIndex;
private int cursor;
PlainItr(ArrayList<Value[]> orderedRows, int startIndex, int endIndex) {
super(orderedRows);
this.endIndex = endIndex;
cursor = startIndex;
}
@Override
public boolean hasNext() {
return cursor <= endIndex;
}
@Override
public Value[] next() {
if (cursor > endIndex) {
throw new NoSuchElementException();
}
return orderedRows.get(cursor++);
}
}
private final class PlainReverseItr extends Itr {
private final int startIndex;
private int cursor;
PlainReverseItr(ArrayList<Value[]> orderedRows, int startIndex, int endIndex) {
super(orderedRows);
this.startIndex = startIndex;
cursor = endIndex;
}
@Override
public boolean hasNext() {
return cursor >= startIndex;
}
@Override
public Value[] next() {
if (cursor < startIndex) {
throw new NoSuchElementException();
}
return orderedRows.get(cursor--);
}
}
private abstract class AbstractBitSetItr extends Itr {
final BitSet set;
int cursor;
AbstractBitSetItr(ArrayList<Value[]> orderedRows, BitSet set) {
super(orderedRows);
this.set = set;
}
@Override
public final boolean hasNext() {
return cursor >= 0;
}
}
private final class BitSetItr extends AbstractBitSetItr {
BitSetItr(ArrayList<Value[]> orderedRows, BitSet set) {
super(orderedRows, set);
cursor = set.nextSetBit(0);
}
@Override
public Value[] next() {
if (cursor < 0) {
throw new NoSuchElementException();
}
Value[] result = orderedRows.get(cursor);
cursor = set.nextSetBit(cursor + 1);
return result;
}
}
private final class BitSetReverseItr extends AbstractBitSetItr {
BitSetReverseItr(ArrayList<Value[]> orderedRows, BitSet set) {
super(orderedRows, set);
cursor = set.length() - 1;
}
@Override
public Value[] next() {
if (cursor < 0) {
throw new NoSuchElementException();
}
Value[] result = orderedRows.get(cursor);
cursor = set.previousSetBit(cursor - 1);
return result;
}
}
private final SimpleExtent extent; private final SimpleExtent extent;
private final WindowFrameExclusion exclusion;
/** /**
* Creates new instance of window frame clause. * Creates new instance of window frame clause.
* *
* @param extent * @param extent
* window frame extent * window frame extent
* @param exclusion
* window frame exclusion
*/ */
public WindowFrame(SimpleExtent extent) { public WindowFrame(SimpleExtent extent, WindowFrameExclusion exclusion) {
this.extent = extent; this.extent = extent;
this.exclusion = exclusion;
} }
/** /**
...@@ -77,7 +254,8 @@ public final class WindowFrame { ...@@ -77,7 +254,8 @@ public final class WindowFrame {
* @return whether window frame specification can be omitted * @return whether window frame specification can be omitted
*/ */
public boolean isDefault() { public boolean isDefault() {
return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW; return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW
&& exclusion == WindowFrameExclusion.EXCLUDE_NO_OTHERS;
} }
/** /**
...@@ -87,7 +265,8 @@ public final class WindowFrame { ...@@ -87,7 +265,8 @@ public final class WindowFrame {
* @return whether window frame specification contains all rows in partition * @return whether window frame specification contains all rows in partition
*/ */
public boolean isFullPartition() { public boolean isFullPartition() {
return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING; return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING
&& exclusion == WindowFrameExclusion.EXCLUDE_NO_OTHERS;
} }
/** /**
...@@ -95,11 +274,16 @@ public final class WindowFrame { ...@@ -95,11 +274,16 @@ public final class WindowFrame {
* *
* @param orderedRows * @param orderedRows
* ordered rows * ordered rows
* @param sortOrder
* sort order
* @param currentRow * @param currentRow
* index of the current row * index of the current row
* @param reverse
* whether iterator should iterate in reverse order
* @return iterator * @return iterator
*/ */
public Iterator<Value[]> iterator(final ArrayList<Value[]> orderedRows, int currentRow) { public Iterator<Value[]> iterator(ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow,
boolean reverse) {
int size = orderedRows.size(); int size = orderedRows.size();
final int startIndex, endIndex; final int startIndex, endIndex;
switch (extent) { switch (extent) {
...@@ -118,82 +302,45 @@ public final class WindowFrame { ...@@ -118,82 +302,45 @@ public final class WindowFrame {
default: default:
throw DbException.getUnsupportedException("window frame extent =" + extent); throw DbException.getUnsupportedException("window frame extent =" + extent);
} }
return new Iterator<Value[]>() { if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) {
return complexIterator(orderedRows, sortOrder, currentRow, startIndex, endIndex, reverse);
private int cursor = startIndex; }
return reverse ? new PlainReverseItr(orderedRows, startIndex, endIndex)
@Override : new PlainItr(orderedRows, startIndex, endIndex);
public boolean hasNext() {
return cursor <= endIndex;
}
@Override
public Value[] next() {
if (cursor > endIndex) {
throw new NoSuchElementException();
}
return orderedRows.get(cursor++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
} }
/** private Iterator<Value[]> complexIterator(ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow,
* Returns iterator in descending order. int startIndex, int endIndex, boolean reverse) {
*
* @param orderedRows
* ordered rows
* @param currentRow
* index of the current row
* @return iterator in descending order
*/
public Iterator<Value[]> reverseIterator(final ArrayList<Value[]> orderedRows, int currentRow) {
int size = orderedRows.size(); int size = orderedRows.size();
final int startIndex, endIndex; BitSet set = new BitSet(size);
switch (extent) { set.set(startIndex, endIndex + 1);
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW: switch (exclusion) {
startIndex = 0; case EXCLUDE_CURRENT_ROW:
endIndex = currentRow; set.clear(currentRow);
break;
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING:
startIndex = currentRow;
endIndex = size - 1;
break; break;
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING: case EXCLUDE_GROUP:
startIndex = 0; case EXCLUDE_TIES: {
endIndex = size - 1; int exStart = currentRow;
break; Value[] row = orderedRows.get(currentRow);
default: while (exStart > startIndex && sortOrder.compare(row, orderedRows.get(exStart - 1)) == 0) {
throw DbException.getUnsupportedException("window frame extent =" + extent); exStart--;
}
return new Iterator<Value[]>() {
private int cursor = endIndex;
@Override
public boolean hasNext() {
return cursor >= startIndex;
} }
int exEnd = currentRow;
@Override while (exEnd < endIndex && sortOrder.compare(row, orderedRows.get(exEnd + 1)) == 0) {
public Value[] next() { exEnd++;
if (cursor < startIndex) {
throw new NoSuchElementException();
}
return orderedRows.get(cursor--);
} }
set.clear(exStart, exEnd + 1);
@Override if (exclusion == WindowFrameExclusion.EXCLUDE_TIES) {
public void remove() { set.set(currentRow);
throw new UnsupportedOperationException();
} }
}
}; //$FALL-THROUGH$
default:
}
if (set.isEmpty()) {
return Collections.emptyIterator();
}
return reverse ? new BitSetReverseItr(orderedRows, set) : new BitSetItr(orderedRows, set);
} }
/** /**
...@@ -203,7 +350,11 @@ public final class WindowFrame { ...@@ -203,7 +350,11 @@ public final class WindowFrame {
* @see org.h2.expression.Expression#getSQL() * @see org.h2.expression.Expression#getSQL()
*/ */
public String getSQL() { public String getSQL() {
return extent.getSQL(); String sql = extent.getSQL();
if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) {
sql = sql + ' ' + exclusion.getSQL();
}
return sql;
} }
} }
...@@ -321,11 +321,11 @@ public class WindowFunction extends AbstractAggregate { ...@@ -321,11 +321,11 @@ public class WindowFunction extends AbstractAggregate {
Value v; Value v;
switch (type) { switch (type) {
case FIRST_VALUE: { case FIRST_VALUE: {
v = getNthValue(frame.iterator(ordered, i), 0, ignoreNulls); v = getNthValue(frame.iterator(ordered, getOverOrderBySort(), i, false), 0, ignoreNulls);
break; break;
} }
case LAST_VALUE: case LAST_VALUE:
v = getNthValue(frame.reverseIterator(ordered, i), 0, ignoreNulls); v = getNthValue(frame.iterator(ordered, getOverOrderBySort(), i, true), 0, ignoreNulls);
break; break;
case NTH_VALUE: { case NTH_VALUE: {
int n = row[1].getInt(); int n = row[1].getInt();
...@@ -333,7 +333,7 @@ public class WindowFunction extends AbstractAggregate { ...@@ -333,7 +333,7 @@ public class WindowFunction extends AbstractAggregate {
throw DbException.getInvalidValueException("nth row", n); throw DbException.getInvalidValueException("nth row", n);
} }
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); v = getNthValue(iter, n, ignoreNulls);
break; break;
} }
......
...@@ -233,8 +233,8 @@ SELECT ...@@ -233,8 +233,8 @@ SELECT
> rows: 6 > rows: 6
SELECT ARRAY_AGG(SUM(ID)) OVER(ORDER /**/ BY ID) FROM TEST GROUP BY ID; 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)
> (1, 2) > (1, 2)
> (1, 2, 3) > (1, 2, 3)
...@@ -245,3 +245,33 @@ SELECT ARRAY_AGG(SUM(ID)) OVER(ORDER /**/ BY ID) FROM TEST GROUP BY ID; ...@@ -245,3 +245,33 @@ SELECT ARRAY_AGG(SUM(ID)) OVER(ORDER /**/ BY ID) FROM TEST GROUP BY ID;
DROP TABLE TEST; DROP TABLE TEST;
> ok > 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 *, ...@@ -135,5 +135,16 @@ SELECT *,
> 13 4 null 41 null null > 13 4 null 41 null null
> rows (ordered): 13 > 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; DROP TABLE TEST;
> ok > ok
...@@ -796,4 +796,4 @@ interior envelopes multilinestring multipoint packed exterior normalization awkw ...@@ -796,4 +796,4 @@ interior envelopes multilinestring multipoint packed exterior normalization awkw
xym normalizes coord setz xyzm geometrycollection multipolygon mixup rings polygons rejection finite xym normalizes coord setz xyzm geometrycollection multipolygon mixup rings polygons rejection finite
pointzm pointz pointm dimensionality redefine forum measures pointzm pointz pointm dimensionality redefine forum measures
mpg casted pzm mls constrained subtypes complains 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论