提交 8aaaec64 authored 作者: Thomas Mueller's avatar Thomas Mueller

Optimization for OR: (X=1 OR X=2) is converted to (X IN(1, 2)).

上级 d7856843
...@@ -27,6 +27,9 @@ Change Log ...@@ -27,6 +27,9 @@ Change Log
start a transaction, insert many rows, delete many rows, rollback. The number of rows depends start a transaction, insert many rows, delete many rows, rollback. The number of rows depends
on the cache size. on the cache size.
</li><li>The stack trace of very common exceptions is no longer written to the .trace.db file by default. </li><li>The stack trace of very common exceptions is no longer written to the .trace.db file by default.
</li><li>An optimization for OR is implemented, but disabled by default.
Expressions of the type X=1 OR X=2 are converted to X IN(1, 2).
To enable, set the system property h2.optimizeInList to true before loading the H2 JDBC driver.
</li><li>An optimization for IN(..) and IN(SELECT...) is implemented, but disabled by default. </li><li>An optimization for IN(..) and IN(SELECT...) is implemented, but disabled by default.
To enable, set the system property h2.optimizeInList to true before loading the H2 JDBC driver. To enable, set the system property h2.optimizeInList to true before loading the H2 JDBC driver.
If enabled, this overrides h2.optimizeIn and h2.optimizeInJoin. Unlike now, this optimization If enabled, this overrides h2.optimizeIn and h2.optimizeInJoin. Unlike now, this optimization
......
...@@ -462,18 +462,24 @@ public class SysProperties { ...@@ -462,18 +462,24 @@ public class SysProperties {
*/ */
public static final boolean OPTIMIZE_MIN_MAX = getBooleanSetting("h2.optimizeMinMax", true); public static final boolean OPTIMIZE_MIN_MAX = getBooleanSetting("h2.optimizeMinMax", true);
/**
* System property <code>h2.optimizeSubqueryCache</code> (default: true).<br />
* Cache subquery results.
*/
public static final boolean OPTIMIZE_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true);
/** /**
* System property <code>h2.optimizeNot</code> (default: true).<br /> * System property <code>h2.optimizeNot</code> (default: true).<br />
* Optimize NOT conditions by removing the NOT and inverting the condition. * Optimize NOT conditions by removing the NOT and inverting the condition.
*/ */
public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true); public static final boolean OPTIMIZE_NOT = getBooleanSetting("h2.optimizeNot", true);
/**
* System property <code>h2.optimizeOr</code> (default: false).<br />
* Convert (C=? OR C=?) to (C IN(?, ?)).
*/
public static final boolean OPTIMIZE_OR = getBooleanSetting("h2.optimizeOr", false);
/**
* System property <code>h2.optimizeSubqueryCache</code> (default: true).<br />
* Cache subquery results.
*/
public static final boolean OPTIMIZE_SUBQUERY_CACHE = getBooleanSetting("h2.optimizeSubqueryCache", true);
/** /**
* System property <code>h2.optimizeTwoEquals</code> (default: true).<br /> * System property <code>h2.optimizeTwoEquals</code> (default: true).<br />
* Optimize expressions of the form A=B AND B=1. In this case, AND A=1 is * Optimize expressions of the form A=B AND B=1. In this case, AND A=1 is
......
...@@ -16,6 +16,7 @@ import org.h2.index.IndexCondition; ...@@ -16,6 +16,7 @@ import org.h2.index.IndexCondition;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueBoolean; import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -427,15 +428,36 @@ public class Comparison extends Condition { ...@@ -427,15 +428,36 @@ public class Comparison extends Condition {
return left.getCost() + (right == null ? 0 : right.getCost()) + 1; return left.getCost() + (right == null ? 0 : right.getCost()) + 1;
} }
/**
* Get the other expression if this is an equals comparison and the other
* expression matches.
*
* @param match the expression that should match
* @return null if no match, the other expression if there is a match
*/
Expression getIfEquals(Expression match) {
if (compareType == EQUAL) {
String sql = match.getSQL();
if (left.getSQL().equals(sql)) {
return right;
} else if (right.getSQL().equals(sql)) {
return left;
}
}
return null;
}
/** /**
* Get an additional condition if possible. Example: given two conditions * Get an additional condition if possible. Example: given two conditions
* A=B and B=C, the new condition A=C is returned. * A=B AND B=C, the new condition A=C is returned. Given the two conditions
* A=1 OR A=2, the new condition A IN(1, 2) is returned.
* *
* @param session the session * @param session the session
* @param other the second condition * @param other the second condition
* @param add true for AND, false for OR
* @return null or the third condition * @return null or the third condition
*/ */
Comparison getAdditional(Session session, Comparison other) { Expression getAdditional(Session session, Comparison other, boolean and) {
if (compareType == other.compareType && compareType == EQUAL) { if (compareType == other.compareType && compareType == EQUAL) {
boolean lc = left.isConstant(), rc = right.isConstant(); boolean lc = left.isConstant(), rc = right.isConstant();
boolean l2c = other.left.isConstant(), r2c = other.right.isConstant(); boolean l2c = other.left.isConstant(), r2c = other.right.isConstant();
...@@ -443,6 +465,8 @@ public class Comparison extends Condition { ...@@ -443,6 +465,8 @@ public class Comparison extends Condition {
String l2 = other.left.getSQL(); String l2 = other.left.getSQL();
String r = right.getSQL(); String r = right.getSQL();
String r2 = other.right.getSQL(); String r2 = other.right.getSQL();
if (and) {
// a=b AND a=c
// must not compare constants. example: NOT(B=2 AND B=3) // must not compare constants. example: NOT(B=2 AND B=3)
if (!(rc && r2c) && l.equals(l2)) { if (!(rc && r2c) && l.equals(l2)) {
return new Comparison(session, EQUAL, right, other.right); return new Comparison(session, EQUAL, right, other.right);
...@@ -453,6 +477,19 @@ public class Comparison extends Condition { ...@@ -453,6 +477,19 @@ public class Comparison extends Condition {
} else if (!(lc && l2c) && r.equals(r2)) { } else if (!(lc && l2c) && r.equals(r2)) {
return new Comparison(session, EQUAL, left, other.left); return new Comparison(session, EQUAL, left, other.left);
} }
} else {
// a=b OR a=c
Database db = session.getDatabase();
if (rc && r2c && l.equals(l2)) {
return new ConditionIn(db, left, ObjectArray.newInstance(right, other.right));
} else if (rc && l2c && l.equals(r2)) {
return new ConditionIn(db, left, ObjectArray.newInstance(right, other.left));
} else if (lc && r2c && r.equals(l2)) {
return new ConditionIn(db, right, ObjectArray.newInstance(left, other.right));
} else if (lc && l2c && r.equals(r2)) {
return new ConditionIn(db, right, ObjectArray.newInstance(left, other.left));
}
}
} }
return null; return null;
} }
......
...@@ -126,7 +126,7 @@ public class ConditionAndOr extends Condition { ...@@ -126,7 +126,7 @@ public class ConditionAndOr extends Condition {
public Expression optimize(Session session) throws SQLException { public Expression optimize(Session session) throws SQLException {
// TODO NULL: see wikipedia, // TODO NULL: see wikipedia,
// http://www-cs-students.stanford.edu/~wlam/compsci/sqlnulls // http://www-cs-students.stanford.edu/~wlam/compsci/sqlnulls
// TODO test if all optimizations are switched off against all on // TODO test all optimizations switched off against all on
// (including performance) // (including performance)
left = left.optimize(session); left = left.optimize(session);
right = right.optimize(session); right = right.optimize(session);
...@@ -142,12 +142,12 @@ public class ConditionAndOr extends Condition { ...@@ -142,12 +142,12 @@ public class ConditionAndOr extends Condition {
// INSERT INTO TEST VALUES(1, NULL); // INSERT INTO TEST VALUES(1, NULL);
// SELECT * FROM TEST WHERE NOT (B=A AND B=0); // no rows // SELECT * FROM TEST WHERE NOT (B=A AND B=0); // no rows
// SELECT * FROM TEST WHERE NOT (B=A AND B=0 AND A=0); // 1, NULL // SELECT * FROM TEST WHERE NOT (B=A AND B=0 AND A=0); // 1, NULL
if (SysProperties.OPTIMIZE_NOT && SysProperties.OPTIMIZE_TWO_EQUALS && andOrType == AND) { if (SysProperties.OPTIMIZE_TWO_EQUALS && SysProperties.OPTIMIZE_NOT && andOrType == AND) {
// try to add conditions (A=B AND B=1: add A=1) // try to add conditions (A=B AND B=1: add A=1)
if (left instanceof Comparison && right instanceof Comparison) { if (left instanceof Comparison && right instanceof Comparison) {
Comparison compLeft = (Comparison) left; Comparison compLeft = (Comparison) left;
Comparison compRight = (Comparison) right; Comparison compRight = (Comparison) right;
Expression added = compLeft.getAdditional(session, compRight); Expression added = compLeft.getAdditional(session, compRight, true);
if (added != null) { if (added != null) {
added = added.optimize(session); added = added.optimize(session);
ConditionAndOr a = new ConditionAndOr(AND, this, added); ConditionAndOr a = new ConditionAndOr(AND, this, added);
...@@ -157,6 +157,27 @@ public class ConditionAndOr extends Condition { ...@@ -157,6 +157,27 @@ public class ConditionAndOr extends Condition {
} }
// TODO optimization: convert ((A=1 AND B=2) OR (A=1 AND B=3)) to // TODO optimization: convert ((A=1 AND B=2) OR (A=1 AND B=3)) to
// (A=1 AND (B=2 OR B=3)) // (A=1 AND (B=2 OR B=3))
if (SysProperties.OPTIMIZE_OR && andOrType == OR) {
// try to add conditions (A=B AND B=1: add A=1)
if (left instanceof Comparison && right instanceof Comparison) {
Comparison compLeft = (Comparison) left;
Comparison compRight = (Comparison) right;
Expression added = compLeft.getAdditional(session, compRight, false);
if (added != null) {
return added.optimize(session);
}
} else if (left instanceof ConditionIn && right instanceof Comparison) {
Expression added = ((ConditionIn) left).getAdditional(session, (Comparison) right);
if (added != null) {
return added.optimize(session);
}
} else if (right instanceof ConditionIn && left instanceof Comparison) {
Expression added = ((ConditionIn) right).getAdditional(session, (Comparison) left);
if (added != null) {
return added.optimize(session);
}
}
}
// TODO optimization: convert .. OR .. to UNION if the cost is lower // TODO optimization: convert .. OR .. to UNION if the cost is lower
Value l = left.isConstant() ? left.getValue(session) : null; Value l = left.isConstant() ? left.getValue(session) : null;
Value r = right.isConstant() ? right.getValue(session) : null; Value r = right.isConstant() ? right.getValue(session) : null;
......
...@@ -258,4 +258,21 @@ public class ConditionIn extends Condition { ...@@ -258,4 +258,21 @@ public class ConditionIn extends Condition {
return new ConditionAndOr(ConditionAndOr.AND, this, on); return new ConditionAndOr(ConditionAndOr.AND, this, on);
} }
/**
* Add an additional element if possible. Example: given two conditions
* A IN(1, 2) OR A=3, the constant 3 is added: A IN(1, 2, 3).
*
* @param session the session
* @param other the second condition
* @return null if the condition was not added, or the new condition
*/
public Expression getAdditional(Session session, Comparison other) {
Expression add = other.getIfEquals(left);
if (add != null) {
valueList.add(add);
return this;
}
return null;
}
} }
...@@ -9,7 +9,6 @@ package org.h2.util; ...@@ -9,7 +9,6 @@ package org.h2.util;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
/** /**
...@@ -47,6 +46,20 @@ public class ObjectArray<T> implements Iterable<T> { ...@@ -47,6 +46,20 @@ public class ObjectArray<T> implements Iterable<T> {
return new ObjectArray<T>(CAPACITY_INIT); return new ObjectArray<T>(CAPACITY_INIT);
} }
/**
* Create a new object with the given values.
*
* @param list the initial elements
* @return the object
*/
public static <T> ObjectArray<T> newInstance(T... list) {
ObjectArray<T> t = new ObjectArray<T>(CAPACITY_INIT);
for (T x : list) {
t.add(x);
}
return t;
}
/** /**
* Create a new object with the default initial capacity. * Create a new object with the default initial capacity.
* *
......
...@@ -295,9 +295,14 @@ java org.h2.test.TestAll timer ...@@ -295,9 +295,14 @@ java org.h2.test.TestAll timer
/* /*
improve LIKE performance
System.setProperty("h2.optimizeInList", "true"); System.setProperty("h2.optimizeInList", "true");
System.setProperty("h2.optimizeOr", "true");
optimization for X IN(..) and OR:
add more test cases, code coverage 100%
document in optimizations.sql
out of memory in tests: analyze heap dump
------------- -------------
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论