Enhance the ANALYZE command so that it gathers statistics in the sqlite_stat1

table even for tables that are empty or have no indices.

FossilOrigin-Name: a7645d293801da64a7579737d0a8b48117af2e2c
This commit is contained in:
drh 2010-09-25 22:32:56 +00:00
parent 42825cd215
commit 155640552b
11 changed files with 114 additions and 67 deletions

View File

@ -1,5 +1,8 @@
C Add\snew\sfile\se_createtable.test.
D 2010-09-25T17:29:58
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
C Enhance\sthe\sANALYZE\scommand\sso\sthat\sit\sgathers\sstatistics\sin\sthe\ssqlite_stat1\ntable\seven\sfor\stables\sthat\sare\sempty\sor\shave\sno\sindices.
D 2010-09-25T22:32:56
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in c599a15d268b1db2aeadea19df2adc3bf2eb6bee
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -112,7 +115,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c 8dc27638e7e2553e80b2b621f232be5eb1e85ef3
F src/analyze.c da65ce99bb159b10e85a1e460adbe53a88062500
F src/analyze.c df69779a9bad368de5f5a32b8c733282d8dcbbbe
F src/attach.c c689d516ee8cc52bf11bef2067d76eb8b716228a
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c d5b0137bc20327af08c14772227cc35134839c30
@ -121,7 +124,7 @@ F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
F src/btree.c d878577184112d982d00ea05afcc7487cd9f06f5
F src/btree.h 2d1a83ad509047e8cc314fda7e054f99ff52414d
F src/btreeInt.h c424f2f131cc61ddf130f9bd736b3df12c8a51f0
F src/build.c 5acc8a7d79ca81102a5d020fbafb7a4162f96d1d
F src/build.c 99894b89b7943b6f8337757facc4e61cef1d9508
F src/callback.c a1d1b1c9c85415dff013af033e2fed9c8382d33b
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
F src/ctime.c 4f3aadad62c6c9f0d4e5a96718516ac4e3c598df
@ -177,7 +180,7 @@ F src/select.c b0b124781474e4e0c8f64022875e5e2009e13443
F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056
F src/sqlite.h.in dae3f74d7b2b516967ede39b8e503718b571d9da
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
F src/sqliteInt.h ef7ed8746759c09edd87e5550de973a4da3e4ca7
F src/sqliteInt.h d8ae67308fec3f2abdce817a630aceae42ad8043
F src/sqliteLimit.h a17dcd3fb775d63b64a43a55c54cb282f9726f44
F src/status.c 496913d4e8441195f6f2a75b1c95993a45b9b30b
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@ -237,7 +240,7 @@ F src/vtab.c 0e8e0cb30dffb078367e843e84e37ef99236c7e4
F src/wal.c 7081f148cb52b0cf2280e6384196402dc58130a3
F src/wal.h 96669b645e27cd5a111ba59f0cae7743a207bc3c
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c a5040c004496d456761e8f10750f648bbd84e982
F src/where.c 34c733f9e8ca5e8f611958422e7cc236bc9cf739
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
@ -246,7 +249,7 @@ F test/alter2.test 75f731508f1bf27ba09a6075c66cd02216ba464b
F test/alter3.test 8677e48d95536f7a6ed86a1a774744dadcc22b07
F test/alter4.test 1e5dd6b951e9f65ca66422edff02e56df82dd403
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/analyze.test bf692e7db414f268a136bade16c03a1bdbb9240c
F test/analyze.test c1eb87067fc16ece7c07e823d6395fd831b270c5
F test/analyze2.test 59dac6c399c0c5d1a90a11ee7cc606743fb6db93
F test/analyze3.test 6d4f4b0929545a9d1af803a0608a0c51b92a3537
F test/async.test ad4ba51b77cd118911a3fe1356b0809da9c108c3
@ -258,7 +261,7 @@ F test/attach.test ce9660e51768fab93cf129787be886c5d6c4fd81
F test/attach2.test a295d2d7061adcee5884ef4a93c7c96a82765437
F test/attach3.test bd9830bc3a0d22ed1310c9bff6896927937017dc
F test/attachmalloc.test 38d2da5fdaf09ba0add57296967a3061e5842584
F test/auth.test 8f21c160a4562f54f27618e85bac869efcecbcaf
F test/auth.test 26cc6f219580191539bf335abe03e55e49310846
F test/auth2.test 270baddc8b9c273682760cffba6739d907bd2882
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
F test/autoinc.test 85ef3180a737e6580086a018c09c6f1a52759b46
@ -543,7 +546,7 @@ F test/minmax3.test 66a60eb0f20281b0753249d347c5de0766954cee
F test/misc1.test e56baf44656dd68d6475a4b44521045a60241e9b
F test/misc2.test a628db7b03e18973e5d446c67696b03de718c9fd
F test/misc3.test 72c5dc87a78e7865c5ec7a969fc572913dbe96b6
F test/misc4.test 91e8ed25c092c2bb4e0bb01864631e2930f8d7de
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test c5f4e6a82e04e71820c0f9f64f6733f04c8ae0ae
@ -600,7 +603,7 @@ F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test 44aa6e7110592e18110b0b9cf5c024d37d23be17
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test 2b5e8500d8ec3dd4c8e0c99eb1431b3d11fcc24c
F test/select6.test cc25a8650cf9a4d4f74e586c45a75f9836516b18
F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
F test/select9.test 74c0fb2c6eecb0219cbed0cbe3df136f8fbf9343
@ -736,7 +739,7 @@ F test/tkt35xx.test ed9721bd9eb1693b3b4d3cf2a093fa7f92af0c93
F test/tkt3630.test 929f64852103054125200bc825c316d5f75d42f7
F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a
F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
F test/tkt3757.test 8f2208930655bbd4f92c14e19e72303a43e098ef
F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595
F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df
F test/tkt3773.test 430b06567ce40285dfd2c4834a2a61816403efeb
@ -867,7 +870,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 44deaaefeeb95827daeaf84aa5e205b456e75b40
R f9cdaa74878a8325422c228d4f8c7dbc
U dan
Z 39756bc936cfb3dbb51046b9bdefa610
P 20e16fef55c355a1d7e97d0c390769b941e83fdb
R 96b44d864f75b35f18ba4c7ab3226d4c
U drh
Z e1547e0aba5840882b569e70505d3edd
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFMnnieoxKgR168RlERAs7CAJ9XFT8WTA9gYf6noiCftlq/XsWqugCfaGoh
P4TBzZ9lm4AFS3sK2f1UkgA=
=phK5
-----END PGP SIGNATURE-----

View File

@ -1 +1 @@
20e16fef55c355a1d7e97d0c390769b941e83fdb
a7645d293801da64a7579737d0a8b48117af2e2c

View File

@ -114,6 +114,7 @@ static void analyzeOneTable(
int topOfLoop; /* The top of the loop */
int endOfLoop; /* The end of the loop */
int addr; /* The address of an instruction */
int jZeroRows = 0; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
@ -132,8 +133,15 @@ static void analyzeOneTable(
#endif
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){
/* Do no analysis for tables that have no indices */
if( v==0 || NEVER(pTab==0) ){
return;
}
if( pTab->pSelect ){
/* Do not gather statistics on views */
return;
}
if( memcmp(pTab->zName, "sqlite_", 7)==0 ){
/* Do not gather statistics on system tables */
return;
}
assert( sqlite3BtreeHoldsAllMutexes(db) );
@ -150,6 +158,7 @@ static void analyzeOneTable(
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
iIdxCur = pParse->nTab++;
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol = pIdx->nColumn;
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
@ -164,10 +173,7 @@ static void analyzeOneTable(
(char *)pKey, P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIdx->zName));
/* Populate the registers containing the table and index names. */
if( pTab->pIndex==pIdx ){
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
}
/* Populate the register containing the index name. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
#ifdef SQLITE_ENABLE_STAT2
@ -302,8 +308,10 @@ static void analyzeOneTable(
** If K>0 then it is always the case the D>0 so division by zero
** is never possible.
*/
addr = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
if( jZeroRows==0 ){
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
}
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
@ -317,13 +325,35 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
}
/* If the table has no indices, create a single sqlite_stat1 entry
** containing NULL as the index name and the row count as the content.
*/
if( pTab->pIndex==0 ){
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
VdbeComment((v, "%s", pTab->zName));
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
}else{
assert( jZeroRows>0 );
addr = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, jZeroRows);
}
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
if( pParse->nMem<regRec ) pParse->nMem = regRec;
if( jZeroRows ){
sqlite3VdbeJumpHere(v, addr);
}
}
/*
** Generate code that will cause the most recent index analysis to
** be laoded into internal hash tables where is can be used.
** be loaded into internal hash tables where is can be used.
*/
static void loadAnalysis(Parse *pParse, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
@ -453,33 +483,46 @@ struct analysisInfo {
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
** argv[0] = name of the index
** argv[1] = results of analysis - on integer for each column
** argv[0] = name of the table
** argv[1] = name of the index (might be NULL)
** argv[2] = results of analysis - on integer for each column
**
** Entries for which argv[1]==NULL simply record the number of rows in
** the table.
*/
static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
analysisInfo *pInfo = (analysisInfo*)pData;
Index *pIndex;
int i, c;
Table *pTable;
int i, c, n;
unsigned int v;
const char *z;
assert( argc==2 );
assert( argc==3 );
UNUSED_PARAMETER2(NotUsed, argc);
if( argv==0 || argv[0]==0 || argv[1]==0 ){
if( argv==0 || argv[0]==0 || argv[2]==0 ){
return 0;
}
pIndex = sqlite3FindIndex(pInfo->db, argv[0], pInfo->zDatabase);
if( pIndex==0 ){
pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase);
if( pTable==0 ){
return 0;
}
z = argv[1];
for(i=0; *z && i<=pIndex->nColumn; i++){
if( argv[1] ){
pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
}else{
pIndex = 0;
}
n = pIndex ? pIndex->nColumn : 0;
z = argv[2];
for(i=0; *z && i<=n; i++){
v = 0;
while( (c=z[0])>='0' && c<='9' ){
v = v*10 + c - '0';
z++;
}
if( i==0 ) pTable->nRowEst = v;
if( pIndex==0 ) break;
pIndex->aiRowEst[i] = v;
if( *z==' ' ) z++;
}
@ -555,7 +598,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load new statistics out of the sqlite_stat1 table */
zSql = sqlite3MPrintf(db,
"SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
"SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{

View File

@ -802,6 +802,7 @@ void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
pTable->nRowEst = 1000000;
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
@ -2830,14 +2831,14 @@ exit_create_index:
void sqlite3DefaultRowEst(Index *pIdx){
unsigned *a = pIdx->aiRowEst;
int i;
unsigned n;
assert( a!=0 );
a[0] = 1000000;
for(i=pIdx->nColumn; i>=5; i--){
a[i] = 5;
}
while( i>=1 ){
a[i] = 11 - i;
i--;
a[0] = pIdx->pTable->nRowEst;
if( a[0]<10 ) a[0] = 10;
n = 10;
for(i=1; i<=pIdx->nColumn; i++){
a[i] = n;
if( n>5 ) n--;
}
if( pIdx->onError!=OE_None ){
a[pIdx->nColumn] = 1;

View File

@ -1252,6 +1252,7 @@ struct Table {
Column *aCol; /* Information about each column */
Index *pIndex; /* List of SQL indexes on this table. */
int tnum; /* Root BTree node for this table (see note above) */
unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
u16 nRef; /* Number of pointers to this Table */
u8 tabFlags; /* Mask of TF_* values */

View File

@ -1706,7 +1706,7 @@ static void bestAutomaticIndex(
assert( pParse->nQueryLoop >= (double)1 );
pTable = pSrc->pTab;
nTableRow = pTable->pIndex ? pTable->pIndex->aiRowEst[0] : 1000000;
nTableRow = pTable->nRowEst;
logN = estLog(nTableRow);
costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1);
if( costTempIdx>=pCost->rCost ){
@ -2513,23 +2513,14 @@ static void bestBtreeIndex(
sPk.nColumn = 1;
sPk.aiColumn = &aiColumnPk;
sPk.aiRowEst = aiRowEstPk;
aiRowEstPk[1] = 1;
sPk.onError = OE_Replace;
sPk.pTable = pSrc->pTab;
aiRowEstPk[0] = pSrc->pTab->nRowEst;
aiRowEstPk[1] = 1;
pFirst = pSrc->pTab->pIndex;
if( pSrc->notIndexed==0 ){
sPk.pNext = pFirst;
}
/* The aiRowEstPk[0] is an estimate of the total number of rows in the
** table. Get this information from the ANALYZE information if it is
** available. If not available, assume the table 1 million rows in size.
*/
if( pFirst ){
assert( pFirst->aiRowEst!=0 ); /* Allocated together with pFirst */
aiRowEstPk[0] = pFirst->aiRowEst[0];
}else{
aiRowEstPk[0] = 1000000;
}
pProbe = &sPk;
wsFlagMask = ~(
WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE
@ -4103,7 +4094,7 @@ WhereInfo *sqlite3WhereBegin(
**
** The best strategy is to iterate through table t1 first. However it
** is not possible to determine this with a simple greedy algorithm.
** However, since the cost of a linear scan through table t2 is the same
** Since the cost of a linear scan through table t2 is the same
** as the cost of a linear scan through table t1, a simple greedy
** algorithm may choose to use t2 for the outer loop, which is a much
** costlier approach.

View File

@ -73,7 +73,7 @@ do_test analyze-1.6.3 {
} {1 {table sqlite_stat1 may not be indexed}}
do_test analyze-1.7 {
execsql {
SELECT * FROM sqlite_stat1
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
}
} {}
do_test analyze-1.8 {
@ -83,7 +83,7 @@ do_test analyze-1.8 {
} {0 {}}
do_test analyze-1.9 {
execsql {
SELECT * FROM sqlite_stat1
SELECT * FROM sqlite_stat1 WHERE idx NOT NULL
}
} {}
do_test analyze-1.10 {
@ -96,7 +96,7 @@ do_test analyze-1.11 {
execsql {
SELECT * FROM sqlite_stat1
}
} {}
} {t1 {} 0}
do_test analyze-1.12 {
catchsql {
ANALYZE t1;
@ -106,7 +106,7 @@ do_test analyze-1.13 {
execsql {
SELECT * FROM sqlite_stat1
}
} {}
} {t1 {} 0}
# Create some indices that can be analyzed. But do not yet add
# data. Without data in the tables, no analysis is done.
@ -117,21 +117,21 @@ do_test analyze-2.1 {
ANALYZE main.t1;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
} {}
} {t1 {} 0}
do_test analyze-2.2 {
execsql {
CREATE INDEX t1i2 ON t1(b);
ANALYZE t1;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
} {}
} {t1 {} 0}
do_test analyze-2.3 {
execsql {
CREATE INDEX t1i3 ON t1(a,b);
ANALYZE main;
SELECT * FROM sqlite_stat1 ORDER BY idx;
}
} {}
} {t1 {} 0}
# Start adding data to the table. Verify that the analysis
# is done correctly.

View File

@ -1976,12 +1976,12 @@ ifcapable analyze {
ANALYZE;
}
set ::authargs
} {t4 {} main {}}
} {t4 {} main {} t2 {} main {}}
do_test auth-1.295 {
execsql {
SELECT count(*) FROM sqlite_stat1;
}
} 2
} 3
proc auth {code args} {
if {$code=="SQLITE_ANALYZE"} {
set ::authargs [concat $::authargs $args]
@ -1999,7 +1999,7 @@ ifcapable analyze {
execsql {
SELECT count(*) FROM sqlite_stat1;
}
} 2
} 3
} ;# ifcapable analyze

View File

@ -151,7 +151,7 @@ ifcapable subquery {
select a.*, x.*
from a, (select key,sum(period) from b group by key) as x
where a.key=x.key;
where a.key=x.key order by 1 desc;
}
} {01 data01 01 3 +1 data+1 +1 7}

View File

@ -458,6 +458,7 @@ do_test select6-8.6 {
do_test select6-9.1 {
execsql {
SELECT a.x, b.x FROM t1 AS a, (SELECT x FROM t1 LIMIT 2) AS b
ORDER BY 1, 2
}
} {1 1 1 2 2 1 2 2 3 1 3 2 4 1 4 2}
do_test select6-9.2 {

View File

@ -35,9 +35,9 @@ do_test tkt3757-1.1 {
CREATE TABLE t2(a INTEGER, b TEXT);
INSERT INTO t2 VALUES(2, 'two');
ANALYZE;
SELECT * FROM sqlite_stat1;
SELECT * FROM sqlite_stat1 ORDER BY 1, 2;
}
} {t1 t1i1 {1 1 1}}
} {t1 t1i1 {1 1 1} t2 {} 1}
# Modify statistics in order to make the optimizer then that:
#