Compare commits

...

7 Commits

Author SHA1 Message Date
drh f7d4578313 Simplify the computation of Index.aAvgEq.
FossilOrigin-Name: c448873006cda294cdaf6b61e75a07f121ba34ea
2016-03-04 19:55:02 +00:00
drh 4e62a13216 Merge changes from trunk.
FossilOrigin-Name: 5294c977d9b15a4f29028782ef253329bd739fc0
2016-03-04 18:45:12 +00:00
drh 268df2d77f Change the estimated row counts in stat1 to be one-third worst-case and
two-threads average case.

FossilOrigin-Name: 21bfd47c4245846b12aeeb7cf0212529e300b878
2016-03-01 14:31:59 +00:00
drh 2c809de2bd Fix test cases to align with the improved stats computation.
FossilOrigin-Name: 810967bff6ba262f38a2833e2d4efef4ef00f463
2016-03-01 12:45:08 +00:00
drh b80ffe626c Improvements to the logic for adding the "noskipscan" flag to stat1 entries.
FossilOrigin-Name: 421b5b544af734b97e3da47699fa0f82b21617d3
2016-02-29 23:02:50 +00:00
drh fc0cb3a0b8 The ANALYZE command automatically appends "noskipscan" to sqlite_stat1 entries
that have large worst-case repeat estimates but small average repeat estimates.

FossilOrigin-Name: 6326ba5891fae9c6be0c0c51cebcbe44c9f3f057
2016-02-29 21:27:16 +00:00
drh 0cf8896ec8 Modify the ANALYZE command to store worst-case statistics in sqlite_stat1,
rather thn average case.

FossilOrigin-Name: 5a0143c94ec0682798f3c09fba63593e695d2e2d
2016-02-29 18:30:30 +00:00
6 changed files with 128 additions and 126 deletions
+9 -10
View File
@@ -1,5 +1,5 @@
C Defer\sopening\sand\swriting\sstatement\sjournals\suntil\sthe\ssize\sreaches\sa\nthreshold\s(currently\s64KiB).
D 2016-03-04T14:43:44.681
C Simplify\sthe\scomputation\sof\sIndex.aAvgEq.
D 2016-03-04T19:55:02.239
F Makefile.in ead489fe5d3313b8a2a8d5f3710173a4b50dd2e0
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc d68db8ddac21ea2576ea5b446f12cf9ebe897a03
@@ -286,7 +286,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c 1bb0709b3048e24217b80ec6bd78a3e99a47c01b
F src/analyze.c ab57b6763dd4c6170a20673d14882c033affd188
F src/analyze.c a89554ee91a8b17b8a745770ddcb4e85a1e27a69
F src/attach.c a3724c64de1099d85e30751213d285752aed9505
F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240
F src/backup.c f60f0aa55d25d853ffde53d0b0370a7bb7ee41ce
@@ -354,7 +354,7 @@ F src/shell.c 5e0ab1e708dc294330ccd8230536e1801f60822e
F src/sqlite.h.in 57d2a02b14c9ec4f7cb294153eaf62294dc5aa68
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h dfbe62ffd95b99afe2140d8c35b180d11924072d
F src/sqliteInt.h 8a4810fa90b17eb08acab43649b877e894ea1e98
F src/sqliteInt.h 7d1257c165974c64d4715d80ee1122aba4578b4e
F src/sqliteLimit.h 7b28cf72cbd52f178bfc97ea266445e351f2cd24
F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
@@ -429,7 +429,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 10deb6b43887662691e5f53d10b3c171c401169b
F src/wal.h 2f7c831cf3b071fa548bf2d5cac640846a7ff19c
F src/walker.c 0f142b5bd3ed2041fc52d773880748b212e63354
F src/where.c 56948ada5aacc3bf2628db3776986e8bf4085383
F src/where.c 2b61974a8442f2c7afb3905322b56d3d618d2804
F src/whereInt.h 93297d56edd137b7ea004490690fb6e2ce028a34
F src/wherecode.c 3ca820435c5b597bb50e63ed11e938786fe5c23e
F src/whereexpr.c fb87944b1254234e5bba671aaf6dee476241506a
@@ -1032,7 +1032,7 @@ F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
F test/skipscan1.test d37a75b4be4eb9dedeb69b4f38b1d0a74b5021d7
F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
F test/skipscan2.test 4cf572db43ca6b2f3b8162295e984c3555e4abeb
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
@@ -1453,8 +1453,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 2682e8e413fadbca0673f242769af17bfd291559 5b2fe5219ab9ad15969e3374894a75979b65cb3c
R bcfeac6deb0b24b3190e8b427e406c58
T +closed 5b2fe5219ab9ad15969e3374894a75979b65cb3c
P 5294c977d9b15a4f29028782ef253329bd739fc0
R 0d9736ab8ee345d5b31279fe6ba1cf6e
U drh
Z 04f483c25715ded0ed1694d083bfc434
Z 7c24b575cc7f48aed8f15dbed80ebac8
+1 -1
View File
@@ -1 +1 @@
cb9302cca423de41305719a49208daa392ec09da
c448873006cda294cdaf6b61e75a07f121ba34ea
+77 -51
View File
@@ -267,6 +267,7 @@ typedef struct Stat4Sample Stat4Sample;
struct Stat4Sample {
tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
tRowcnt *amxEq; /* Maximum length run of equal values */
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
@@ -418,6 +419,7 @@ static void statInit(
/* Allocate the space required for the Stat4Accum object */
n = sizeof(*p)
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.amxEq */
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
@@ -436,18 +438,19 @@ static void statInit(
p->nRow = 0;
p->nCol = nCol;
p->nKeyCol = nKeyCol;
p->current.anDLt = (tRowcnt*)&p[1];
p->current.anEq = &p->current.anDLt[nColUp];
p->current.anEq = (tRowcnt*)&p[1];
p->current.amxEq = &p->current.anEq[nColUp];
p->current.anDLt = &p->current.amxEq[nColUp];
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
{
u8 *pSpace; /* Allocated space not yet assigned */
int i; /* Used to iterate through p->aSample[] */
int i; /* Loop counter */
p->iGet = -1;
p->mxSample = mxSample;
p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
p->current.anLt = &p->current.anEq[nColUp];
p->current.anLt = &p->current.anDLt[nColUp];
p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
/* Set up the Stat4Accum.a[] and aBest[] arrays */
@@ -721,7 +724,10 @@ static void statPush(
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
for(i=0; i<p->nCol; i++){
p->current.anEq[i] = 1;
p->current.amxEq[i] = 1;
}
}else{
/* Second and subsequent calls get processed here */
samplePushPrevious(p, iChng);
@@ -729,6 +735,7 @@ static void statPush(
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
for(i=0; i<iChng; i++){
if( p->current.amxEq[i]==p->current.anEq[i] ) p->current.amxEq[i]++;
p->current.anEq[i]++;
}
for(i=iChng; i<p->nCol; i++){
@@ -821,31 +828,53 @@ static void statGet(
assert( argc==1 );
#endif
{
/* Return the value to store in the "stat" column of the sqlite_stat1
/* Return a value for the "stat" column of the sqlite_stat1
** table for this index.
**
** The value is a string composed of a list of integers describing
** the index. The first integer in the list is the total number of
** entries in the index. There is one additional integer in the list
** for each indexed column. This additional integer is an estimate of
** the number of rows matched by a stabbing query on the index using
** a key with the corresponding number of fields. In other words,
** if the index is on columns (a,b) and the sqlite_stat1 value is
** "100 10 2", then SQLite estimates that:
** the index. The first integer is the (estimated) total number
** entries in the index. There is one additional integer for each
** column in the index. The first added integer is an estimate of
** the number of rows that match a single key in the first column of
** the index. The second added integer is an estimate on of the number
** of rows that match a single key consisting of the first two columns
** of the index. And so forth.
**
** * the index contains 100 rows,
** * "WHERE a=?" matches 10 rows, and
** * "WHERE a=? AND b=?" matches 2 rows.
** For example, for an index on columns (a,b), if the sqlite_stat1.stat
** values is "100 10 2", that means there are about 100 rows in the
** index, and that a query against a=$key1 will match about 10 rows
** and a query against "a=$key1 AND b=$key2" will match about 2 rows.
**
** If D is the count of distinct values and K is the total number of
** rows, then each estimate is computed as:
** Let V be the average number of rows that match a key, and let M
** be the most number of rows that match the key for any possible value
** of that key. The estimate is computed as:
**
** I = (K+D-1)/D
** E = (2*V + M)/3
**
** Consider two indexes. Index X has with 100 values of exactly 0 and
** 100 singleton values between 1 and 100. Index Y has 200 values
** evenly distributed between 1 and 20. If only the average (V) is
** used in the estimate, X would have "200 2" and Y would have "200 10"
** and so the planner would think X is the more selective index. And
** X often would be more selective. But when searching for 0, index X
** would perform badly. To avoid this problem, the M is added into the
** estimate so that the stat for X is "200 34" and Y is still "200 10".
** In this way, Y is the preferred index (all else being equal) and
** the pathological case is avoided.
**
** For deciding whether or not to do a skip-scan, we want to know the
** average number of rows (V) with the same key, not the mixed estimate
** E shown above. Usually E will be close enough. However, if E is
** large but V is small, that could trick the query planner into thinking
** that a skip-scan might work well on this index. To avoid that, the
** "noskipscan" flag is added in cases where the divergence between E
** and V might mislead the query planner.
*/
char *z;
int i;
int noSkipScan = 0;
char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 );
char *zRet = sqlite3MallocZero( (p->nKeyCol+2)*25 );
if( zRet==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -854,14 +883,18 @@ static void statGet(
sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
z = zRet + sqlite3Strlen30(zRet);
for(i=0; i<p->nKeyCol; i++){
u64 nDistinct = p->current.anDLt[i] + 1;
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
u64 nDistinct = p->current.anDLt[i];
u64 iMx = p->current.amxEq[i]; /* M: Most rows per key */
u64 iAvg = (p->nRow+nDistinct)/(nDistinct+1); /* V: Average per key */
u64 iVal = (iMx+iAvg*2)/3; /* E: The estimate */
sqlite3_snprintf(24, z, " %llu", iVal);
z += sqlite3Strlen30(z);
assert( p->current.anEq[i] );
if( iVal>=WHERE_SKIPSCAN_ONSET && iAvg<(WHERE_SKIPSCAN_ONSET*2/3) ){
noSkipScan = 1;
}
}
assert( z[0]=='\0' && z>zRet );
if( noSkipScan ) sqlite3_snprintf(14, z, " noskipscan");
sqlite3_result_text(context, zRet, -1, sqlite3_free);
}
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
@@ -1571,52 +1604,45 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
*/
static void initAvgEq(Index *pIdx){
if( pIdx ){
IndexSample *aSample = pIdx->aSample;
IndexSample *pFinal = &aSample[pIdx->nSample-1];
int iCol;
int nCol = 1;
if( pIdx->nSampleCol>1 ){
int nSample = pIdx->nSample; /* Number of samples */
int nCol = pIdx->nSampleCol; /* Number of columns sampled */
IndexSample *aSample = pIdx->aSample; /* The samples */
IndexSample *pFinal = &aSample[nSample-1]; /* The last sample */
int iCol; /* Loop counter of columns */
if( nCol>1 ){
/* If this is stat4 data, then calculate aAvgEq[] values for all
** sample columns except the last. The last is always set to 1, as
** once the trailing PK fields are considered all index keys are
** unique. */
nCol = pIdx->nSampleCol-1;
nCol--;
pIdx->aAvgEq[nCol] = 1;
}
for(iCol=0; iCol<nCol; iCol++){
int nSample = pIdx->nSample;
int i; /* Used to iterate through samples */
u32 nDSample = 0; /* Number of distinct samples less than pFinal*/
tRowcnt sumEq = 0; /* Sum of the nEq values */
tRowcnt avgEq = 0;
tRowcnt avgEq; /* Average repeats for unsampled entries */
tRowcnt nRow; /* Number of rows in index */
i64 nSum100 = 0; /* Number of terms contributing to sumEq */
i64 nDist100; /* Number of distinct values in index */
tRowcnt prevLt = 0; /* Number of values less than previous sample */
tRowcnt thisLt; /* Number of values less than current sample */
if( !pIdx->aiRowEst || iCol>=pIdx->nKeyCol || pIdx->aiRowEst[iCol+1]==0 ){
nRow = pFinal->anLt[iCol];
nDist100 = (i64)100 * pFinal->anDLt[iCol];
nSample--;
nRow = pFinal->anLt[iCol] + pFinal->anEq[iCol];
}else{
nRow = pIdx->aiRowEst[0];
nDist100 = ((i64)100 * pIdx->aiRowEst[0]) / pIdx->aiRowEst[iCol+1];
}
pIdx->nRowEst0 = nRow;
/* Set nSum to the number of distinct (iCol+1) field prefixes that
** occur in the stat4 table for this index. Set sumEq to the sum of
** the nEq values for column iCol for the same set (adding the value
** only once where there exist duplicate prefixes). */
for(i=0; i<nSample; i++){
if( i==(pIdx->nSample-1)
|| aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol]
){
for(i=0; (thisLt = aSample[i].anLt[iCol])<pFinal->anLt[iCol]; i++){
if( i==0 || thisLt!=prevLt ){
sumEq += aSample[i].anEq[iCol];
nSum100 += 100;
nDSample++;
}
prevLt = thisLt;
}
if( nDist100>nSum100 ){
avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100);
if( pFinal->anDLt[iCol] > nDSample ){
avgEq = (pFinal->anLt[iCol] - sumEq)/(pFinal->anDLt[iCol] - nDSample);
}else{
avgEq = 1;
}
if( avgEq==0 ) avgEq = 1;
pIdx->aAvgEq[iCol] = avgEq;
+9
View File
@@ -2421,6 +2421,15 @@ struct SrcList {
#define JT_OUTER 0x0020 /* The "OUTER" keyword is present */
#define JT_ERROR 0x0040 /* unknown or unsupported join type */
/*
** TUNING: The skip-scan optimization is only profitable if the average
** number of repeats of an entry in the index is greater than or equal to
** WHERE_SKIPSCAN_ONSET. If the average numbe of repeats is less
** than WHERE_SKIPSCAN_ONSET, then it is faster to do a full table
** scan.
*/
#define WHERE_SKIPSCAN_ONSET 18
#define WHERE_SKIPSCAN_ONSET_LOG 42
/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
+2 -2
View File
@@ -2412,11 +2412,11 @@ static int whereLoopAddBtreeIndex(
** the code). And, even if it is not, it should not be too much slower.
** On the other hand, the extra seeks could end up being significantly
** more expensive. */
assert( 42==sqlite3LogEst(18) );
assert( WHERE_SKIPSCAN_ONSET_LOG==sqlite3LogEst(WHERE_SKIPSCAN_ONSET) );
if( saved_nEq==saved_nSkip
&& saved_nEq+1<pProbe->nKeyCol
&& pProbe->noSkipScan==0
&& pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
&& pProbe->aiRowLogEst[saved_nEq+1]>=WHERE_SKIPSCAN_ONSET_LOG
&& (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
){
LogEst nIter;
+30 -62
View File
@@ -48,22 +48,16 @@ do_execsql_test skipscan2-1.2 {
INSERT INTO people VALUES('Robert','student',159);
INSERT INTO people VALUES('Sally','student',166);
INSERT INTO people VALUES('Tom','student',171);
INSERT INTO people VALUES('Ursula','student',170);
INSERT INTO people VALUES('Vance','student',179);
INSERT INTO people VALUES('Willma','student',175);
INSERT INTO people VALUES('Xavier','teacher',185);
INSERT INTO people VALUES('Yvonne','student',149);
INSERT INTO people VALUES('Zach','student',170);
}
# Without ANALYZE, a skip-scan is not used
#
do_execsql_test skipscan2-1.3 {
SELECT name FROM people WHERE height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
SELECT name FROM people WHERE height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-1.3eqp {
EXPLAIN QUERY PLAN
SELECT name FROM people WHERE height>=180 ORDER BY +name;
SELECT name FROM people WHERE height<=161 ORDER BY +name;
} {~/*INDEX people_idx1 */}
# Now do an ANALYZE. A skip-scan can be used after ANALYZE.
@@ -79,11 +73,11 @@ do_execsql_test skipscan2-1.4 {
}
db cache flush
do_execsql_test skipscan2-1.5 {
SELECT name FROM people WHERE height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
SELECT name FROM people WHERE height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-1.5eqp {
EXPLAIN QUERY PLAN
SELECT name FROM people WHERE height>=180 ORDER BY +name;
SELECT name FROM people WHERE height<=161 ORDER BY +name;
} {/*INDEX people_idx1 */}
# Same answer with other formulations of the same query
@@ -91,59 +85,33 @@ do_execsql_test skipscan2-1.5eqp {
do_execsql_test skipscan2-1.6 {
SELECT name FROM people
WHERE role IN (SELECT DISTINCT role FROM people)
AND height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
AND height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-1.7 {
SELECT name FROM people WHERE role='teacher' AND height>=180
SELECT name FROM people WHERE role='teacher' AND height<=161
UNION ALL
SELECT name FROM people WHERE role='student' AND height>=180
SELECT name FROM people WHERE role='student' AND height<=161
ORDER BY 1;
} {David Jack Patrick Quiana Xavier}
} {Alice Bob Cindy Emily Megan Olivia Robert}
# Add 8 more people, bringing the total to 34. Then the number of
# duplicates in the left-column of the index will be 17 and
# skip-scan should not be used after an (unfudged) ANALYZE.
# Add more people so that the estimated number of rows per key is
# equal to the skip-scan threshold of 18. Then run an (unfudged)
# ANALYZE. skip-scan should work.
#
do_execsql_test skipscan2-1.8 {
INSERT INTO people VALUES('Angie','student',166);
INSERT INTO people VALUES('Brad','student',176);
INSERT INTO people VALUES('Claire','student',168);
INSERT INTO people VALUES('Donald','student',162);
INSERT INTO people VALUES('Elaine','student',177);
INSERT INTO people VALUES('Frazier','student',159);
INSERT INTO people VALUES('Grace','student',179);
INSERT INTO people VALUES('Horace','student',166);
INSERT INTO people VALUES('Ursula','student',170);
ANALYZE;
SELECT stat FROM sqlite_stat1 WHERE idx='people_idx1';
} {{34 17 2}}
} {{21 13 2}}
db cache flush
do_execsql_test skipscan2-1.9 {
SELECT name FROM people WHERE height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
SELECT name FROM people WHERE height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-1.9eqp {
EXPLAIN QUERY PLAN
SELECT name FROM people WHERE height>=180 ORDER BY +name;
SELECT name FROM people WHERE height<=161 ORDER BY +name;
} {~/*INDEX people_idx1 */}
# Add 2 more people, bringing the total to 36. Then the number of
# duplicates in the left-column of the index will be 18 and
# skip-scan will be used after an (unfudged) ANALYZE.
#
do_execsql_test skipscan2-1.10 {
INSERT INTO people VALUES('Ingrad','student',155);
INSERT INTO people VALUES('Jacob','student',179);
ANALYZE;
SELECT stat FROM sqlite_stat1 WHERE idx='people_idx1';
} {{36 18 2}}
db cache flush
do_execsql_test skipscan2-1.11 {
SELECT name FROM people WHERE height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
do_execsql_test skipscan2-1.11eqp {
EXPLAIN QUERY PLAN
SELECT name FROM people WHERE height>=180 ORDER BY +name;
} {/*INDEX people_idx1 */}
# Repeat using a WITHOUT ROWID table.
#
@@ -158,19 +126,19 @@ do_execsql_test skipscan2-2.1 {
INSERT INTO peoplew(name,role,height)
SELECT name, role, height FROM people;
ALTER TABLE people RENAME TO old_people;
SELECT name FROM peoplew WHERE height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
SELECT name FROM peoplew WHERE height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-2.2 {
SELECT name FROM peoplew
WHERE role IN (SELECT DISTINCT role FROM peoplew)
AND height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
AND height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-2.2 {
SELECT name FROM peoplew WHERE role='teacher' AND height>=180
SELECT name FROM peoplew WHERE role='teacher' AND height<=161
UNION ALL
SELECT name FROM peoplew WHERE role='student' AND height>=180
SELECT name FROM peoplew WHERE role='student' AND height<=161
ORDER BY 1;
} {David Jack Patrick Quiana Xavier}
} {Alice Bob Cindy Emily Megan Olivia Robert}
# Now do an ANALYZE. A skip-scan can be used after ANALYZE.
#
@@ -179,12 +147,12 @@ do_execsql_test skipscan2-2.4 {
}
db cache flush
do_execsql_test skipscan2-2.5 {
SELECT name FROM peoplew WHERE height>=180 ORDER BY +name;
} {David Jack Patrick Quiana Xavier}
SELECT name FROM peoplew WHERE height<=161 ORDER BY +name;
} {Alice Bob Cindy Emily Megan Olivia Robert}
do_execsql_test skipscan2-2.5eqp {
EXPLAIN QUERY PLAN
SELECT name FROM peoplew WHERE height>=180 ORDER BY +name;
} {/*INDEX peoplew_idx1 */}
SELECT name FROM peoplew WHERE height<=161 ORDER BY +name;
} {/*INDEX peoplew_idx1*/}
# A skip-scan on a PK index of a WITHOUT ROWID table.
#