When the estimated cost to do a sort overwhelms the estimated cost to do

individual table lookups, make sure that the table lookup costs are still
taken into consideration when selecting the lookup algorithm.

FossilOrigin-Name: ec5d84ba69c100d9565425ed74040a49e410ea03
This commit is contained in:
drh 2014-08-07 16:50:00 +00:00
parent 858b638d1f
commit ddef5dc044
4 changed files with 83 additions and 16 deletions

View File

@ -1,5 +1,5 @@
C A\scouple\smore\sharmless\scompiler\swarnings\seliminated.
D 2014-08-06T18:50:51.299
C When\sthe\sestimated\scost\sto\sdo\sa\ssort\soverwhelms\sthe\sestimated\scost\sto\sdo\nindividual\stable\slookups,\smake\ssure\sthat\sthe\stable\slookup\scosts\sare\sstill\ntaken\sinto\sconsideration\swhen\sselecting\sthe\slookup\salgorithm.
D 2014-08-07T16:50:00.581
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5eb79e334a5de69c87740edd56af6527dd219308
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -296,7 +296,7 @@ F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
F src/wal.c 264df50a1b33124130b23180ded2e2c5663c652a
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
F src/where.c ce1b9a3a2573033cd15e0882719db7f211f21cdd
F src/where.c 5a099c6d2a1bfbf46bc64979e89330dee3a1f3b9
F src/whereInt.h 929c1349b5355fd44f22cee5c14d72b3329c58a6
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@ -1117,7 +1117,7 @@ F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
F test/whereJ.test 7dde28284d20f358b559ca592e294db03e1d7103
F test/whereJ.test 35a40a50d0e13aa6b0de7cc5d4b204e5f9f9669f
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
@ -1185,7 +1185,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P a2a60307ea68a3230952a56cb65369ba0a208967
R 1442001d079992194e2cf55642b869fa
P bcf6d775f90f4d1ba018a1b965f2f710df130f01
R da285ec7c1bbef437389fa691998cee0
U drh
Z bb5786216c21ae2a69b537cdb50bf939
Z 083887f7ebac8cb5fec0c6d1cdc7c751

View File

@ -1 +1 @@
bcf6d775f90f4d1ba018a1b965f2f710df130f01
ec5d84ba69c100d9565425ed74040a49e410ea03

View File

@ -5424,6 +5424,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
LogEst rCost; /* Cost of a path */
LogEst nOut; /* Number of outputs */
LogEst mxCost = 0; /* Maximum cost of a set of paths */
LogEst mxOut = 0; /* nOut value for maximum cost path */
int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
WherePath *aFrom; /* All nFrom paths at the previous level */
WherePath *aTo; /* The nTo best paths at the current level */
@ -5441,7 +5442,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** For joins of 3 or more tables, track the 10 best paths */
mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10);
assert( nLoop<=pWInfo->pTabList->nSrc );
WHERETRACE(0x002, ("---- begin solver\n"));
WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst));
/* Allocate and initialize space for aTo and aFrom */
ii = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
@ -5529,7 +5530,13 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}else{
revMask = pFrom->revLoop;
}
/* Check to see if pWLoop should be added to the mxChoice best so far */
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
** First look for an existing path among best-so-far paths
** that covers the same set of loops and has the same isOrdered
** setting as the current path candidate.
*/
for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
if( pTo->maskLoop==maskNew
&& ((pTo->isOrdered^isOrdered)&80)==0
@ -5539,7 +5546,14 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
}
if( jj>=nTo ){
if( nTo>=mxChoice && rCost>=mxCost ){
/* None of the existing best-so-far paths match the candidate. */
if( nTo>=mxChoice && rCost==mxCost ) printf("nOut=%d mxOut=%d\n", nOut, mxOut);
if( nTo>=mxChoice
&& (rCost>mxCost || (rCost==mxCost && nOut>=mxOut))
){
/* The current candidate is no better than any of the mxChoice
** paths currently in the best-so-far buffer. So discard
** this candidate as not viable. */
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n",
@ -5549,7 +5563,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
#endif
continue;
}
/* Add a new Path to the aTo[] set */
/* If we reach this points it means that the new candidate path
** needs to be added to the set of best-so-far paths. */
if( nTo<mxChoice ){
/* Increase the size of the aTo set by one */
jj = nTo++;
@ -5566,7 +5581,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
#endif
}else{
if( pTo->rCost<=rCost ){
/* Control reaches here if best-so-far path pTo=aTo[jj] covers the
** same set of loops and has the sam isOrdered setting as the
** candidate path. Check to see if the candidate should replace
** pTo or if the candidate should be skipped */
if( pTo->rCost<rCost || (pTo->rCost==rCost && pTo->nRow<=nOut) ){
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf(
@ -5578,11 +5597,13 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
}
#endif
/* Discard the candidate path from further consideration */
testcase( pTo->rCost==rCost );
continue;
}
testcase( pTo->rCost==rCost+1 );
/* A new and better score for a previously created equivalent path */
/* Control reaches here if the candidate path is better than the
** pTo path. Replace pTo with the candidate. */
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf(
@ -5606,9 +5627,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( nTo>=mxChoice ){
mxI = 0;
mxCost = aTo[0].rCost;
mxOut = aTo[0].nRow;
for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){
if( pTo->rCost>mxCost ){
if( pTo->rCost>mxCost || (pTo->rCost==mxCost && pTo->nRow>mxOut) ){
mxCost = pTo->rCost;
mxOut = pTo->nRow;
mxI = jj;
}
}

View File

@ -9,7 +9,7 @@
#
#***********************************************************************
#
# This file implements a single regression test for a complex
# This file implements regression tests for a complex
# query planning case.
#
@ -328,4 +328,48 @@ do_execsql_test whereJ-1.4 {
GROUP BY aid;
} {/B-TREE/}
############################################################################
# Ensure that the sorting cost does not swamp the loop costs and cause
# distinctions between individual loop costs to get lost, and hence for
# sub-optimal loops to be chosen.
#
do_execsql_test whereJ-2.1 {
CREATE TABLE tab(
id INTEGER PRIMARY KEY,
minChild INTEGER REFERENCES t1,
maxChild INTEGER REFERENCES t1,
x INTEGER
);
EXPLAIN QUERY PLAN
SELECT t4.x
FROM tab AS t0, tab AS t1, tab AS t2, tab AS t3, tab AS t4
WHERE t0.id=0
AND t1.id BETWEEN t0.minChild AND t0.maxChild
AND t2.id BETWEEN t1.minChild AND t1.maxChild
AND t3.id BETWEEN t2.minChild AND t2.maxChild
AND t4.id BETWEEN t3.minChild AND t3.maxChild
ORDER BY t4.x;
} {~/SCAN/}
do_execsql_test whereJ-2.2 {
EXPLAIN QUERY PLAN
SELECT t4.x
FROM tab AS t0a, tab AS t0b,
tab AS t1a, tab AS t1b,
tab AS t2a, tab AS t2b,
tab AS t3a, tab AS t3b,
tab AS t4
WHERE 1
AND t0a.id=1
AND t1a.id BETWEEN t0a.minChild AND t0a.maxChild
AND t2a.id BETWEEN t1a.minChild AND t1a.maxChild
AND t3a.id BETWEEN t2a.minChild AND t2a.maxChild
AND t0b.id=2
AND t1b.id BETWEEN t0b.minChild AND t0b.maxChild
AND t2b.id BETWEEN t1b.minChild AND t1b.maxChild
AND t3b.id BETWEEN t2b.minChild AND t2b.maxChild
AND t4.id BETWEEN t3a.minChild AND t3b.maxChild
ORDER BY t4.x;
} {~/SCAN/}
finish_test