Fix the query planner so that when it has a choice of full-scan tables to
move to the outer loop, it chooses the one that is likely to give the fewest output rows. FossilOrigin-Name: 309bbedf9648c750d7b8aedbc15d4fd68f846824
This commit is contained in:
parent
5e377d90ed
commit
aa0ba4345d
20
manifest
20
manifest
@ -1,8 +1,8 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
C If\sthe\souter\sloop\sof\sa\sjoin\smust\sbe\sa\sfull\stable\sscan,\smake\ssure\sthat\san\nincomplete\sANALYZE\sdoes\snot\strick\sthe\splanner\sinto\suse\sa\stable\sthat\smight\nbe\sindexable\sin\san\sinner\sloop.\s\sTicket\s[13f033c865f878]
|
||||
D 2010-08-04T21:17:16
|
||||
C Fix\sthe\squery\splanner\sso\sthat\swhen\sit\shas\sa\schoice\sof\sfull-scan\stables\sto\nmove\sto\sthe\souter\sloop,\sit\schooses\sthe\sone\sthat\sis\slikely\sto\sgive\sthe\sfewest\noutput\srows.
|
||||
D 2010-08-05T02:52:32
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -233,7 +233,7 @@ F src/vtab.c 82200af3881fa4e1c9cf07cf31d98c09d437e3ab
|
||||
F src/wal.c 0925601f3299c2941a67c9cfff41ee710f70ca82
|
||||
F src/wal.h 906c85760598b18584921fe08008435aa4eeeeb2
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c a1398b29b8ec129ab656ee136e9116fc2f26e3f2
|
||||
F src/where.c a4b2cb14dbd727ff277be3f52851425191a87f66
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
|
||||
@ -801,7 +801,7 @@ F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
|
||||
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
|
||||
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
|
||||
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
|
||||
F test/where3.test acdacc5e1e50ea935b74b3378e4e4b3fa4950092
|
||||
F test/where3.test 3bf8006d441b66a57bee02bb420423f84eb8fde3
|
||||
F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
|
||||
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
|
||||
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
|
||||
@ -844,14 +844,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P 65b8636ac6e5d3e4502d4f576ddf9350d5df3022
|
||||
R f303f8cd6c15f9355d52ea6f34dbc596
|
||||
P e7a714b52c45af096af74049826d32c647abfe3f
|
||||
R 3b6850bff2212e5e06d5e22c17492cd7
|
||||
U drh
|
||||
Z 334acc501b61b4dedf959e66e446e709
|
||||
Z be2f8a9b75ad13b92899d24ed9ea0e24
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
|
||||
iD8DBQFMWdjfoxKgR168RlERAtzHAJsHfo8i9JQtGPiUDENko/1YB6PzvwCfchpH
|
||||
IJ8P1uaP+Eu9M19yQeVqg/o=
|
||||
=L8a0
|
||||
iD8DBQFMWidzoxKgR168RlERAgBpAJ9Bgesz+bY3zJhMYLF/PXRObWtInQCdH7Y+
|
||||
9WSa5R0wAcOA6sAA258SaiU=
|
||||
=4Hzv
|
||||
-----END PGP SIGNATURE-----
|
||||
|
@ -1 +1 @@
|
||||
e7a714b52c45af096af74049826d32c647abfe3f
|
||||
309bbedf9648c750d7b8aedbc15d4fd68f846824
|
39
src/where.c
39
src/where.c
@ -4065,6 +4065,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
Bitmask m; /* Bitmask value for j or bestJ */
|
||||
int isOptimal; /* Iterator for optimal/non-optimal search */
|
||||
int nUnconstrained; /* Number tables without INDEXED BY */
|
||||
Bitmask notIndexed; /* Mask of tables that cannot use an index */
|
||||
|
||||
memset(&bestPlan, 0, sizeof(bestPlan));
|
||||
bestPlan.rCost = SQLITE_BIG_DBL;
|
||||
@ -4107,8 +4108,9 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** costlier approach.
|
||||
*/
|
||||
nUnconstrained = 0;
|
||||
notIndexed = 0;
|
||||
for(isOptimal=(iFrom<nTabList-1); isOptimal>=0; isOptimal--){
|
||||
Bitmask mask; /* Mask of tables not yet ready */
|
||||
Bitmask mask; /* Mask of tables not yet ready */
|
||||
for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
|
||||
int doNotReorder; /* True if this table should not be reordered */
|
||||
WhereCost sCost; /* Cost information from best[Virtual]Index() */
|
||||
@ -4137,28 +4139,43 @@ WhereInfo *sqlite3WhereBegin(
|
||||
}
|
||||
assert( isOptimal || (sCost.used¬Ready)==0 );
|
||||
|
||||
/* If an INDEXED BY clause is present, then the plan must use that
|
||||
** index if it uses any index at all */
|
||||
assert( pTabItem->pIndex==0
|
||||
|| (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
|
||||
|| sCost.plan.u.pIdx==pTabItem->pIndex );
|
||||
|
||||
if( isOptimal && (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
|
||||
notIndexed |= m;
|
||||
}
|
||||
|
||||
/* Conditions under which this table becomes the best so far:
|
||||
**
|
||||
** (1) The table must not depend on other tables that have not
|
||||
** yet run.
|
||||
**
|
||||
** (2) A full-table-scan plan cannot supercede another plan unless
|
||||
** the full-table-scan is an optimal plan.
|
||||
** it is an "optimal" plan as defined above.
|
||||
**
|
||||
** (3) The plan cost must be lower than prior plans or else the
|
||||
** cost must be the same and the number of rows must be lower.
|
||||
**
|
||||
** (4) All tables have an INDEXED BY clause or this table lacks an
|
||||
** (3) All tables have an INDEXED BY clause or this table lacks an
|
||||
** INDEXED BY clause or this table uses the specific
|
||||
** index specified by its INDEXED BY clause.
|
||||
** index specified by its INDEXED BY clause. This rule ensures
|
||||
** that a best-so-far is always selected even if an impossible
|
||||
** combination of INDEXED BY clauses are given. The error
|
||||
** will be detected and relayed back to the application later.
|
||||
** The NEVER() comes about because rule (2) above prevents
|
||||
** An indexable full-table-scan from reaching rule (3).
|
||||
**
|
||||
** (4) The plan cost must be lower than prior plans or else the
|
||||
** cost must be the same and the number of rows must be lower.
|
||||
*/
|
||||
if( (sCost.used¬Ready)==0 /* (1) */
|
||||
&& (bestJ<0 || isOptimal /* (2) */
|
||||
&& (bestJ<0 || (notIndexed&m)!=0 /* (2) */
|
||||
|| (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
|
||||
&& (bestJ<0 || sCost.rCost<bestPlan.rCost /* (3) */
|
||||
&& (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */
|
||||
|| NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
|
||||
&& (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */
|
||||
|| (sCost.rCost<=bestPlan.rCost && sCost.nRow<bestPlan.nRow))
|
||||
&& (nUnconstrained==0 || pTabItem->pIndex==0 /* (4) */
|
||||
|| pTabItem->pIndex==sCost.plan.u.pIdx)
|
||||
){
|
||||
WHERETRACE(("... best so far with cost=%g and nRow=%g\n",
|
||||
sCost.rCost, sCost.nRow));
|
||||
|
@ -235,4 +235,30 @@ do_test where3-3.1 {
|
||||
}
|
||||
} {0 1 {TABLE t302} 1 0 {TABLE t301 USING PRIMARY KEY}}
|
||||
|
||||
# Verify that when there are multiple tables in a join which must be
|
||||
# full table scans that the query planner attempts put the table with
|
||||
# the fewest number of output rows as the outer loop.
|
||||
#
|
||||
do_test where3-4.0 {
|
||||
execsql {
|
||||
CREATE TABLE t400(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t401(p INTEGER PRIMARY KEY, q, r);
|
||||
CREATE TABLE t402(x INTEGER PRIMARY KEY, y, z);
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t400, t401, t402 WHERE t402.z GLOB 'abc*';
|
||||
}
|
||||
} {0 2 {TABLE t402} 1 0 {TABLE t400} 2 1 {TABLE t401}}
|
||||
do_test where3-4.1 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t400, t401, t402 WHERE t401.r GLOB 'abc*';
|
||||
}
|
||||
} {0 1 {TABLE t401} 1 0 {TABLE t400} 2 2 {TABLE t402}}
|
||||
do_test where3-4.2 {
|
||||
execsql {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t400, t401, t402 WHERE t400.c GLOB 'abc*';
|
||||
}
|
||||
} {0 0 {TABLE t400} 1 1 {TABLE t401} 2 2 {TABLE t402}}
|
||||
|
||||
finish_test
|
||||
|
Loading…
Reference in New Issue
Block a user