Optimized SELECT ... ORDER BY X LIMIT Y OFFSET Z queries by partial sort.

上级 e9a290cf
......@@ -52,6 +52,8 @@ Change Log
</li><li>Add a DISK_SPACE_USED system function. Fixes issue 270.
</li><li>Fix a compile-time ambiguity when compiling with JDK7, thanks to a patch from Lukas Eder.
</li><li>Supporting dropping an index for Lucene full-text indexes.
</li><li>Optimized performance for SELECT ... ORDER BY X LIMIT Y OFFSET Z queries for in-memory databases
using partial sort (by Sergi Vladykin).
</li></ul>
<h2>Version 1.3.170 (2012-11-30)</h2>
......
......@@ -335,7 +335,11 @@ public class LocalResult implements ResultInterface, ResultTarget {
external.done();
} else {
if (sort != null) {
sort.sort(rows);
if (offset > 0 || limit > 0) {
sort.sort(rows, offset, limit == -1 ? rows.size() : limit);
} else {
sort.sort(rows);
}
}
}
applyOffset();
......
......@@ -14,6 +14,7 @@ import org.h2.engine.Database;
import org.h2.expression.Expression;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -154,6 +155,29 @@ public class SortOrder implements Comparator<Value[]> {
Collections.sort(rows, this);
}
/**
* Sort a list of rows using offset and limit.
*
* @param rows the list of rows
* @param offset the offset
* @param limit the limit
*/
public void sort(ArrayList<Value[]> rows, int offset, int limit) {
if (rows.isEmpty())
return;
if (limit == 1 && offset == 0) {
rows.set(0, Collections.min(rows, this));
return;
}
Value[][] arr = rows.toArray(new Value[rows.size()][]);
Utils.topNSorted(arr, offset, limit, this);
for (int i = 0, end = Math.min(offset + limit, arr.length); i < end; i++) {
rows.set(i, arr[i]);
}
}
/**
* Get the column index list.
*
......
......@@ -17,6 +17,8 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.zip.ZipEntry;
......@@ -433,6 +435,63 @@ public class Utils {
return new long[len];
}
/**
* Will find top limit values in array using given comparator and place them on positions like if
* full array sort occur. Does not guarantee any sort order of these top elements.
*
* @param array the array.
* @param offset the offset.
* @param limit the limit.
* @param cmp the comparator.
*/
public static <X> void topN(X[] array, int offset, int limit, Comparator<? super X> cmp) {
partialQuickSort(array, 0, array.length - 1, cmp, offset, offset + limit - 1);
}
/**
* The same as {@link #topN(Object[], int, int, Comparator)}} but guarantees sort order of
* found top elements.
*
* @param array the array.
* @param offset the offset.
* @param limit the limit.
* @param cmp the comparator.
*/
public static <X> void topNSorted(X[] array, int offset, int limit, Comparator<? super X> cmp) {
topN(array, offset, limit, cmp);
Arrays.sort(array, offset, Math.min(offset + limit, array.length), cmp);
}
private static <X> void partialQuickSort(X[] array, int low, int high, Comparator<? super X> cmp, int start, int end) {
if (low > end || high < start || (low > start && high < end))
return;
int i = low, j = high;
X pivot = array[low + (high - low) / 2];
while (i <= j) {
while (cmp.compare(array[i], pivot) < 0) {
i++;
}
while (cmp.compare(array[j], pivot) > 0) {
j--;
}
if (i <= j) {
X x = array[i];
array[i] = array[j];
array[j] = x;
i++;
j--;
}
}
if (low < j)
partialQuickSort(array, low, j, cmp, start, end);
if (i < high)
partialQuickSort(array, i, high, cmp, start, end);
}
/**
* Checks if given classes have a common Comparable superclass.
*
......
......@@ -10,6 +10,8 @@ import java.io.File;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.Random;
import org.h2.test.TestBase;
......@@ -40,6 +42,7 @@ public class TestUtils extends TestBase {
testGetNonPrimitiveClass();
testGetNonPrimitiveClass();
testReflectionUtils();
testTopN();
}
private void testWriteReadLong() {
......@@ -59,6 +62,42 @@ public class TestUtils extends TestBase {
}
}
private void testTopN() {
Random rnd = new Random();
Comparator<Integer> cmp = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
for (int z = 0; z < 10000; z++) {
Integer[] arr = new Integer[1 + rnd.nextInt(500)];
for (int i = 0; i < arr.length; i++) {
arr[i] = rnd.nextInt(50);
}
Integer[] arr2 = Arrays.copyOf(arr, arr.length);
int offset = rnd.nextInt(arr.length);
int limit = rnd.nextInt(arr.length);
Utils.topNSorted(arr, offset, limit, cmp);
Arrays.sort(arr2, cmp);
for (int i = offset, end = Math.min(offset + limit, arr.length); i < end; i++) {
if (!arr[i].equals(arr2[i])) {
System.out.println(offset + " " + end);
System.out.println(Arrays.toString(arr));
System.out.println(Arrays.toString(arr2));
fail();
}
}
}
}
private void testGetNonPrimitiveClass() throws Exception {
testGetNonPrimitiveClass(BigInteger.class, BigInteger.class);
testGetNonPrimitiveClass(Boolean.class, boolean.class);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论