Skip to content

Commit

Permalink
优化order by
Browse files Browse the repository at this point in the history
  • Loading branch information
codefollower committed Jul 12, 2015
1 parent b186e44 commit 1e2577e
Show file tree
Hide file tree
Showing 19 changed files with 222 additions and 175 deletions.
Expand Up @@ -6,13 +6,10 @@
*/ */
package org.lealone.dbobject.index; package org.lealone.dbobject.index;


import org.lealone.dbobject.index.IndexBase;
import org.lealone.dbobject.index.Cursor;
import org.lealone.dbobject.index.IndexCondition;
import org.lealone.dbobject.index.IndexType;
import org.lealone.dbobject.table.Column; import org.lealone.dbobject.table.Column;
import org.lealone.dbobject.table.IndexColumn; import org.lealone.dbobject.table.IndexColumn;
import org.lealone.dbobject.table.TableBase; import org.lealone.dbobject.table.TableBase;
import org.lealone.dbobject.table.TableFilter;
import org.lealone.engine.Session; import org.lealone.engine.Session;
import org.lealone.message.DbException; import org.lealone.message.DbException;
import org.lealone.result.Row; import org.lealone.result.Row;
Expand Down Expand Up @@ -108,7 +105,7 @@ public void remove(Session session) {
} }


@Override @Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) { public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
for (Column column : columns) { for (Column column : columns) {
int index = column.getColumnId(); int index = column.getColumnId();
int mask = masks[index]; int mask = masks[index];
Expand Down
Expand Up @@ -7,12 +7,10 @@


import java.util.List; import java.util.List;


import org.lealone.dbobject.index.IndexBase;
import org.lealone.dbobject.index.Cursor;
import org.lealone.dbobject.index.IndexType;
import org.lealone.dbobject.table.Column; import org.lealone.dbobject.table.Column;
import org.lealone.dbobject.table.IndexColumn; import org.lealone.dbobject.table.IndexColumn;
import org.lealone.dbobject.table.MVTable; import org.lealone.dbobject.table.MVTable;
import org.lealone.dbobject.table.TableFilter;
import org.lealone.engine.Session; import org.lealone.engine.Session;
import org.lealone.message.DbException; import org.lealone.message.DbException;
import org.lealone.result.Row; import org.lealone.result.Row;
Expand Down Expand Up @@ -83,14 +81,9 @@ public int getColumnIndex(Column col) {
return -1; return -1;
} }


// @Override
// public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
// return 10 * getCostRangeIndex(masks, mainIndex.getRowCountApproximation(), filter, sortOrder);
// }

@Override @Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) { public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
return 10 * getCostRangeIndex(masks, mainIndex.getRowCountApproximation(), sortOrder); return 10 * getCostRangeIndex(masks, mainIndex.getRowCountApproximation(), filter, sortOrder);
} }


@Override @Override
Expand Down
Expand Up @@ -12,12 +12,10 @@
import java.util.Map.Entry; import java.util.Map.Entry;


import org.lealone.api.ErrorCode; import org.lealone.api.ErrorCode;
import org.lealone.dbobject.index.Cursor;
import org.lealone.dbobject.index.IndexBase;
import org.lealone.dbobject.index.IndexType;
import org.lealone.dbobject.table.Column; import org.lealone.dbobject.table.Column;
import org.lealone.dbobject.table.IndexColumn; import org.lealone.dbobject.table.IndexColumn;
import org.lealone.dbobject.table.MVTable; import org.lealone.dbobject.table.MVTable;
import org.lealone.dbobject.table.TableFilter;
import org.lealone.engine.Constants; import org.lealone.engine.Constants;
import org.lealone.engine.Database; import org.lealone.engine.Database;
import org.lealone.engine.Session; import org.lealone.engine.Session;
Expand Down Expand Up @@ -72,7 +70,7 @@ public MVPrimaryIndex(TransactionStorageEngine storageEngine, Session session, M
ValueDataType valueType = new ValueDataType(db.getCompareMode(), db, sortTypes); ValueDataType valueType = new ValueDataType(db.getCompareMode(), db, sortTypes);
mapName = "table." + getId(); mapName = "table." + getId();
dataMap = storageEngine.openMap(session, mapName, keyType, valueType); dataMap = storageEngine.openMap(session, mapName, keyType, valueType);
//Fix bug in MVStore when creating lots of temporary tables, where we could run out of transaction IDs // Fix bug in MVStore when creating lots of temporary tables, where we could run out of transaction IDs
session.commit(false); session.commit(false);
if (!table.isPersistData()) { if (!table.isPersistData()) {
dataMap.setVolatile(true); dataMap.setVolatile(true);
Expand Down Expand Up @@ -215,18 +213,8 @@ public Row getRow(Session session, long key) {
return row; return row;
} }


// @Override
// public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
// try {
// long cost = 10 * (dataMap.sizeAsLongMax() + Constants.COST_ROW_OFFSET);
// return cost;
// } catch (IllegalStateException e) {
// throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
// }
// }

@Override @Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) { public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
try { try {
long cost = 10 * (dataMap.sizeAsLongMax() + Constants.COST_ROW_OFFSET); long cost = 10 * (dataMap.sizeAsLongMax() + Constants.COST_ROW_OFFSET);
return cost; return cost;
Expand Down
Expand Up @@ -12,12 +12,10 @@
import java.util.TreeSet; import java.util.TreeSet;


import org.lealone.api.ErrorCode; import org.lealone.api.ErrorCode;
import org.lealone.dbobject.index.Cursor;
import org.lealone.dbobject.index.IndexBase;
import org.lealone.dbobject.index.IndexType;
import org.lealone.dbobject.table.Column; import org.lealone.dbobject.table.Column;
import org.lealone.dbobject.table.IndexColumn; import org.lealone.dbobject.table.IndexColumn;
import org.lealone.dbobject.table.MVTable; import org.lealone.dbobject.table.MVTable;
import org.lealone.dbobject.table.TableFilter;
import org.lealone.engine.Database; import org.lealone.engine.Database;
import org.lealone.engine.Session; import org.lealone.engine.Session;
import org.lealone.message.DbException; import org.lealone.message.DbException;
Expand Down Expand Up @@ -69,7 +67,7 @@ public MVSecondaryIndex(TransactionStorageEngine storageEngine, Session session,
ValueDataType keyType = new ValueDataType(db.getCompareMode(), db, sortTypes); ValueDataType keyType = new ValueDataType(db.getCompareMode(), db, sortTypes);
ValueDataType valueType = new ValueDataType(null, null, null); ValueDataType valueType = new ValueDataType(null, null, null);
dataMap = storageEngine.openMap(session, mapName, keyType, valueType); dataMap = storageEngine.openMap(session, mapName, keyType, valueType);
//Fix bug in MVStore when creating lots of temporary tables, where we could run out of transaction IDs // Fix bug in MVStore when creating lots of temporary tables, where we could run out of transaction IDs
session.commit(false); session.commit(false);
if (!keyType.equals(dataMap.getKeyType())) { if (!keyType.equals(dataMap.getKeyType())) {
throw DbException.throwInternalError("Incompatible key type"); throw DbException.throwInternalError("Incompatible key type");
Expand Down Expand Up @@ -156,7 +154,7 @@ public int compareTo(Source o) {
} }
} }


//TODO 不考虑事务 // TODO 不考虑事务
private TransactionMap<Value, Value> openMap(String mapName) { private TransactionMap<Value, Value> openMap(String mapName) {
int[] sortTypes = new int[keyColumns]; int[] sortTypes = new int[keyColumns];
for (int i = 0; i < indexColumns.length; i++) { for (int i = 0; i < indexColumns.length; i++) {
Expand Down Expand Up @@ -348,19 +346,10 @@ public MVTable getTable() {
return mvTable; return mvTable;
} }


// @Override
// public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
// try {
// return 10 * getCostRangeIndex(masks, dataMap.sizeAsLongMax(), filter, sortOrder);
// } catch (IllegalStateException e) {
// throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
// }
// }

@Override @Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) { public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
try { try {
return 10 * getCostRangeIndex(masks, dataMap.sizeAsLongMax(), sortOrder); return 10 * getCostRangeIndex(masks, dataMap.sizeAsLongMax(), filter, sortOrder);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED, e); throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
} }
Expand Down
Expand Up @@ -479,7 +479,7 @@ static SortOrder prepareOrder(Session session, ArrayList<SelectOrderBy> orderLis
} }
sortType[i] = type; sortType[i] = type;
} }
return new SortOrder(session.getDatabase(), index, sortType); return new SortOrder(session.getDatabase(), index, sortType, orderList);
} }


public void setOffset(Expression offset) { public void setOffset(Expression offset) {
Expand Down
Expand Up @@ -373,7 +373,7 @@ public int compare(SchemaObject c1, SchemaObject c2) {
} }


private int generateInsertValues(int count, Table table) throws IOException { private int generateInsertValues(int count, Table table) throws IOException {
PlanItem plan = table.getBestPlanItem(session, null, null); PlanItem plan = table.getBestPlanItem(session, null, null, null);
Index index = plan.getIndex(); Index index = plan.getIndex();
Cursor cursor = index.find(session, null, null); Cursor cursor = index.find(session, null, null);
Column[] columns = table.getColumns(); Column[] columns = table.getColumns();
Expand Down
168 changes: 84 additions & 84 deletions lealone-sql/src/main/java/org/lealone/command/dml/Select.java
Expand Up @@ -506,90 +506,6 @@ private void queryGroup(int columnCount, LocalResult result) {
} }
} }


/**
* Get the index that matches the ORDER BY list, if one exists. This is to
* avoid running a separate ORDER BY if an index can be used. This is
* specially important for large result sets, if only the first few rows are
* important (LIMIT is used)
*
* @return the index if one is found
*/
private Index getSortIndex() {
if (sort == null) {
return null;
}
ArrayList<Column> sortColumns = New.arrayList();
for (int idx : sort.getIndexes()) {
if (idx < 0 || idx >= expressions.size()) {
throw DbException.getInvalidValueException("ORDER BY", idx + 1);
}
Expression expr = expressions.get(idx);
expr = expr.getNonAliasExpression();
if (expr.isConstant()) {
continue;
}
if (!(expr instanceof ExpressionColumn)) {
return null;
}
ExpressionColumn exprCol = (ExpressionColumn) expr;
if (exprCol.getTableFilter() != topTableFilter) {
return null;
}
sortColumns.add(exprCol.getColumn());
}
Column[] sortCols = sortColumns.toArray(new Column[sortColumns.size()]);
int[] sortTypes = sort.getSortTypes();
if (sortCols.length == 0) {
// sort just on constants - can use scan index
return topTableFilter.getTable().getScanIndex(session);
}
ArrayList<Index> list = topTableFilter.getTable().getIndexes();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
Index index = list.get(i);
if (index.getCreateSQL() == null) {
// can't use the scan index
continue;
}
if (index.getIndexType().isHash()) {
continue;
}
IndexColumn[] indexCols = index.getIndexColumns();
if (indexCols.length < sortCols.length) {
continue;
}
boolean ok = true;
for (int j = 0; j < sortCols.length; j++) {
// the index and the sort order must start
// with the exact same columns
IndexColumn idxCol = indexCols[j];
Column sortCol = sortCols[j];
if (idxCol.column != sortCol) {
ok = false;
break;
}
if (idxCol.sortType != sortTypes[j]) {
// NULL FIRST for ascending and NULLS LAST
// for descending would actually match the default
ok = false;
break;
}
}
if (ok) {
return index;
}
}
}
if (sortCols.length == 1 && sortCols[0].getColumnId() == -1) {
// special case: order by _ROWID_
Index index = topTableFilter.getTable().getScanIndex(session);
if (index.isRowIdIndex()) {
return index;
}
}
return null;
}

private void queryDistinct(ResultTarget result, long limitRows) { private void queryDistinct(ResultTarget result, long limitRows) {
// limitRows must be long, otherwise we get an int overflow // limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE // if limitRows is at or near Integer.MAX_VALUE
Expand Down Expand Up @@ -1048,6 +964,90 @@ public void prepare() {
isPrepared = true; isPrepared = true;
} }


/**
* Get the index that matches the ORDER BY list, if one exists. This is to
* avoid running a separate ORDER BY if an index can be used. This is
* specially important for large result sets, if only the first few rows are
* important (LIMIT is used)
*
* @return the index if one is found
*/
private Index getSortIndex() {
if (sort == null) {
return null;
}
ArrayList<Column> sortColumns = New.arrayList();
for (int idx : sort.getQueryColumnIndexes()) {
if (idx < 0 || idx >= expressions.size()) {
throw DbException.getInvalidValueException("ORDER BY", idx + 1);
}
Expression expr = expressions.get(idx);
expr = expr.getNonAliasExpression();
if (expr.isConstant()) {
continue;
}
if (!(expr instanceof ExpressionColumn)) {
return null;
}
ExpressionColumn exprCol = (ExpressionColumn) expr;
if (exprCol.getTableFilter() != topTableFilter) {
return null;
}
sortColumns.add(exprCol.getColumn());
}
Column[] sortCols = sortColumns.toArray(new Column[sortColumns.size()]);
int[] sortTypes = sort.getSortTypes();
if (sortCols.length == 0) {
// sort just on constants - can use scan index
return topTableFilter.getTable().getScanIndex(session);
}
ArrayList<Index> list = topTableFilter.getTable().getIndexes();
if (list != null) {
for (int i = 0, size = list.size(); i < size; i++) {
Index index = list.get(i);
if (index.getCreateSQL() == null) {
// can't use the scan index
continue;
}
if (index.getIndexType().isHash()) {
continue;
}
IndexColumn[] indexCols = index.getIndexColumns();
if (indexCols.length < sortCols.length) {
continue;
}
boolean ok = true;
for (int j = 0; j < sortCols.length; j++) {
// the index and the sort order must start
// with the exact same columns
IndexColumn idxCol = indexCols[j];
Column sortCol = sortCols[j];
if (idxCol.column != sortCol) {
ok = false;
break;
}
if (idxCol.sortType != sortTypes[j]) {
// NULL FIRST for ascending and NULLS LAST
// for descending would actually match the default
ok = false;
break;
}
}
if (ok) {
return index;
}
}
}
if (sortCols.length == 1 && sortCols[0].getColumnId() == -1) {
// special case: order by _ROWID_
Index index = topTableFilter.getTable().getScanIndex(session);
if (index.isRowIdIndex()) {
return index;
}
}
return null;
}

@Override @Override
public double getCost() { public double getCost() {
return cost; return cost;
Expand Down
Expand Up @@ -64,7 +64,7 @@ public void close(Session session) {
} }


@Override @Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) { public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
return 0; return 0;
} }


Expand Down
Expand Up @@ -12,6 +12,7 @@


import org.lealone.dbobject.table.FunctionTable; import org.lealone.dbobject.table.FunctionTable;
import org.lealone.dbobject.table.IndexColumn; import org.lealone.dbobject.table.IndexColumn;
import org.lealone.dbobject.table.TableFilter;
import org.lealone.engine.Session; import org.lealone.engine.Session;
import org.lealone.message.DbException; import org.lealone.message.DbException;
import org.lealone.result.ResultInterface; import org.lealone.result.ResultInterface;
Expand Down Expand Up @@ -58,7 +59,7 @@ public Cursor find(Session session, SearchRow first, SearchRow last) {
} }


@Override @Override
public double getCost(Session session, int[] masks, SortOrder sortOrder) { public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
if (masks != null) { if (masks != null) {
throw DbException.getUnsupportedException("ALIAS"); throw DbException.getUnsupportedException("ALIAS");
} }
Expand Down
Expand Up @@ -80,10 +80,11 @@ public interface Index extends SchemaObject {
* @param session the session * @param session the session
* @param masks per-column comparison bit masks, null means 'always false', * @param masks per-column comparison bit masks, null means 'always false',
* see constants in IndexCondition * see constants in IndexCondition
* @param filter the table filter
* @param sortOrder the sort order * @param sortOrder the sort order
* @return the estimated cost * @return the estimated cost
*/ */
double getCost(Session session, int[] masks, SortOrder sortOrder); double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder);


/** /**
* Remove the index. * Remove the index.
Expand Down

0 comments on commit 1e2577e

Please sign in to comment.