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

Evaluate window aggregate function only once per each group when possible

上级 7aaa5ff4
...@@ -19,6 +19,7 @@ import org.h2.expression.analysis.WindowFrame; ...@@ -19,6 +19,7 @@ import org.h2.expression.analysis.WindowFrame;
import org.h2.expression.analysis.WindowFrameBound; import org.h2.expression.analysis.WindowFrameBound;
import org.h2.expression.analysis.WindowFrameBoundType; import org.h2.expression.analysis.WindowFrameBoundType;
import org.h2.expression.analysis.WindowFrameExclusion; import org.h2.expression.analysis.WindowFrameExclusion;
import org.h2.expression.analysis.WindowFrameUnits;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -86,9 +87,16 @@ public abstract class AbstractAggregate extends DataAnalysisOperation { ...@@ -86,9 +87,16 @@ public abstract class AbstractAggregate extends DataAnalysisOperation {
protected void getOrderedResultLoop(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered, protected void getOrderedResultLoop(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered,
int rowIdColumn) { int rowIdColumn) {
WindowFrame frame = over.getWindowFrame(); WindowFrame frame = over.getWindowFrame();
/*
* With RANGE (default) or GROUPS units and EXCLUDE GROUP or EXCLUDE NO
* OTHERS (default) exclusion all rows in the group have the same value
* of window aggregate function.
*/
boolean grouped = frame == null
|| frame.getUnits() != WindowFrameUnits.ROWS && frame.getExclusion().isGroupOrNoOthers();
if (frame == null) { if (frame == null) {
assert over.getOrderBy() != null; assert over.getOrderBy() != null;
aggregateFastPartition(session, result, ordered, rowIdColumn); aggregateFastPartition(session, result, ordered, rowIdColumn, grouped);
return; return;
} }
if (frame.getStarting().getType() == WindowFrameBoundType.UNBOUNDED_PRECEDING if (frame.getStarting().getType() == WindowFrameBoundType.UNBOUNDED_PRECEDING
...@@ -97,28 +105,28 @@ public abstract class AbstractAggregate extends DataAnalysisOperation { ...@@ -97,28 +105,28 @@ public abstract class AbstractAggregate extends DataAnalysisOperation {
if (following != null && following.getType() == WindowFrameBoundType.UNBOUNDED_FOLLOWING) { if (following != null && following.getType() == WindowFrameBoundType.UNBOUNDED_FOLLOWING) {
aggregateWholePartition(session, result, ordered, rowIdColumn); aggregateWholePartition(session, result, ordered, rowIdColumn);
} else { } else {
aggregateFastPartition(session, result, ordered, rowIdColumn); aggregateFastPartition(session, result, ordered, rowIdColumn, grouped);
} }
return; return;
} }
// All other types of frames (slow) // All other types of frames (slow)
int size = ordered.size(); int size = ordered.size();
for (int i = 0; i < size; i++) { for (int i = 0; i < size;) {
Object aggregateData = createAggregateData(); Object aggregateData = createAggregateData();
for (Iterator<Value[]> iter = WindowFrame.iterator(over, session, ordered, getOverOrderBySort(), i, for (Iterator<Value[]> iter = WindowFrame.iterator(over, session, ordered, getOverOrderBySort(), i,
false); iter.hasNext();) { false); iter.hasNext();) {
updateFromExpressions(session, aggregateData, iter.next()); updateFromExpressions(session, aggregateData, iter.next());
} }
result.put(ordered.get(i)[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData)); i = processGroup(session, result, ordered, rowIdColumn, i, size, aggregateData, grouped);
} }
} }
private void aggregateFastPartition(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered, private void aggregateFastPartition(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered,
int rowIdColumn) { int rowIdColumn, boolean grouped) {
Object aggregateData = createAggregateData(); Object aggregateData = createAggregateData();
int size = ordered.size(); int size = ordered.size();
int lastIncludedRow = -1; int lastIncludedRow = -1;
for (int i = 0; i < size; i++) { for (int i = 0; i < size;) {
int newLast = WindowFrame.getEndIndex(over, session, ordered, getOverOrderBySort(), i); int newLast = WindowFrame.getEndIndex(over, session, ordered, getOverOrderBySort(), i);
assert newLast >= lastIncludedRow; assert newLast >= lastIncludedRow;
if (newLast > lastIncludedRow) { if (newLast > lastIncludedRow) {
...@@ -127,10 +135,21 @@ public abstract class AbstractAggregate extends DataAnalysisOperation { ...@@ -127,10 +135,21 @@ public abstract class AbstractAggregate extends DataAnalysisOperation {
} }
lastIncludedRow = newLast; lastIncludedRow = newLast;
} }
result.put(ordered.get(i)[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData)); i = processGroup(session, result, ordered, rowIdColumn, i, size, aggregateData, grouped);
} }
} }
private int processGroup(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered,
int rowIdColumn, int i, int size, Object aggregateData, boolean grouped) {
Value[] firstRowInGroup = ordered.get(i), currentRowInGroup = firstRowInGroup;
Value r = getAggregatedValue(session, aggregateData);
do {
result.put(currentRowInGroup[rowIdColumn].getInt(), r);
} while (++i < size && grouped
&& overOrderBySort.compare(firstRowInGroup, currentRowInGroup = ordered.get(i)) == 0);
return i;
}
private void aggregateWholePartition(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered, private void aggregateWholePartition(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered,
int rowIdColumn) { int rowIdColumn) {
// Aggregate values from the whole partition // Aggregate values from the whole partition
......
...@@ -38,6 +38,17 @@ public enum WindowFrameExclusion { ...@@ -38,6 +38,17 @@ public enum WindowFrameExclusion {
this.sql = sql; this.sql = sql;
} }
/**
* Returns true if this exclusion clause excludes or includes the whole
* group.
*
* @return true if this exclusion clause is {@link #EXCLUDE_GROUP} or
* {@link #EXCLUDE_NO_OTHERS}
*/
public boolean isGroupOrNoOthers() {
return this == WindowFrameExclusion.EXCLUDE_GROUP || this == EXCLUDE_NO_OTHERS;
}
/** /**
* Returns SQL representation. * Returns SQL representation.
* *
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论