Hello,
I am using sqlite 3.12.2 and I am getting a strange behaviour from a
very simple query. I am trying to understand the exact query plan and
the cause of this problem. I would be grateful if someone could help me.
The query is:
SELECT count(qview1."wlbWellboreName") FROM "discovery" qview2 CROSS
JOIN "wellbore_development_all" qview1 WHERE
(qview1."wlbNpdidWellbore" = qview2."wlbNpdidWellbore");
I have indexes on columns wlbNpdidWellbore in both tables, but I have
not declared any primary keys. On cold cache this query takes almost
three minutes, whereas on warm cache it takes around 100 msec. If I
replace qview1."wlbWellboreName" with * it runs in a couple of seconds
on cold cache. If I create the table "wellbore_development_all" from the
beginning setting wlbNpdidWellbore as primary key, the query runs in
under 3 seconds on cold cache and in a couple of tens of milliseconds on
warm cache.
I have executed analyze and vacuum on both tables before the query, both
with clustered and non clustered indexes. I would normally expect that
the difference would be very small: one more page fetch for each value
of wlbNpdidWellbore that it is matched in the index of
wellbore_development_all
I am pasting the output of .schema and stats for the two tables and also
the output of explain for both cases. From the explain, if I understand
correctly, I can see that we first search the whole
"wellbore_development_all" table (2nd OpenRead) and then the index (3rd
OpenRead). Is that correct? And if so, why is that?
Thanks,
Dimitris
discovery table:
sqlite> .schema
CREATE TABLE discovery( dscName TEXT,cmpLongName
TEXT,dscCurrentActivityStatus TEXT,dscHcType TEXT,wlbName TEXT,nmaName
TEXT,fldName TEXT,dscDateFromInclInField TEXT,dscDiscoveryYear
INTEGER,dscResInclInDiscoveryName TEXT,dscOwnerKind TEXT,dscOwnerName
TEXT,dscNpdidDiscovery INTEGER,fldNpdidField INTEGER,wlbNpdidWellbore
INTEGER,dscFactPageUrl TEXT,dscFactMapUrl TEXT,dscDateUpdated
TEXT,dscDateUpdatedMax TEXT,dateSyncNPD TEXT);
CREATE UNIQUE INDEX "discovery_dscNpdidDiscovery_pkey" ON "discovery"
("dscNpdidDiscovery")
;
CREATE INDEX "IX368_discovery" ON "discovery"
(
"wlbNpdidWellbore"
)
;
CREATE INDEX "IX367_discovery" ON "discovery"
(
"fldNpdidField"
)
;
sqlite> select * from sqlite_stat1;
discovery|IX367_discovery|42100 5
discovery|IX368_discovery|42100 2
discovery|discovery_dscNpdidDiscovery_pkey|42100 1
wellbore_development_all table without primary key declaration:
sqlite> .schema
CREATE TABLE wellbore_development_all( wlbWellboreName TEXT,wlbWell
TEXT,wlbDrillingOperator TEXT,wlbDrillingOperatorGroup
TEXT,wlbProductionLicence TEXT,wlbPurposePlanned TEXT,wlbContent
TEXT,wlbWellType TEXT,wlbEntryDate TEXT,wlbCompletionDate TEXT,wlbField
TEXT,wlbDrillPermit TEXT,wlbDiscovery TEXT,wlbDiscoveryWellbore
TEXT,wlbKellyBushElevation REAL,wlbFinalVerticalDepth REAL,wlbTotalDepth
REAL,wlbWaterDepth REAL,wlbMainArea TEXT,wlbDrillingFacility
TEXT,wlbFacilityTypeDrilling TEXT,wlbProductionFacility
TEXT,wlbLicensingActivity TEXT,wlbMultilateral TEXT,wlbContentPlanned
TEXT,wlbEntryYear INTEGER,wlbCompletionYear
INTEGER,wlbReclassFromWellbore TEXT,wlbPlotSymbol
INTEGER,wlbGeodeticDatum TEXT,wlbNsDeg INTEGER,wlbNsMin INTEGER,wlbNsSec
REAL,wlbNsCode TEXT,wlbEwDeg INTEGER,wlbEwMin INTEGER,wlbEwSec
REAL,wlbEwCode TEXT,wlbNsDecDeg REAL,wlbEwDesDeg REAL,wlbNsUtm
REAL,wlbEwUtm REAL,wlbUtmZone INTEGER,wlbNamePart1 INTEGER,wlbNamePart2
INTEGER,wlbNamePart3 TEXT,wlbNamePart4 INTEGER,wlbNamePart5
TEXT,wlbNamePart6 TEXT,wlbFactPageUrl TEXT,wlbFactMapUrl
TEXT,wlbDiskosWellboreType TEXT,wlbDiskosWellboreParent
TEXT,wlbNpdidWellbore INTEGER,dscNpdidDiscovery INTEGER,fldNpdidField
INTEGER,wlbWdssQcdate TEXT,prlNpdidProductionLicence
INTEGER,fclNpdidFacilityDrilling INTEGER,fclNpdidFacilityProducing
INTEGER,wlbNpdidWellboreReclass INTEGER,wlbDiskosWellOperator
TEXT,wlbDateUpdated TEXT,wlbDateUpdatedMax TEXT,dateSyncNPD TEXT);
CREATE UNIQUE INDEX "wellbore_development_all_wlbNpdidWellbore_pkey" ON
"wellbore_development_all" ("wlbNpdidWellbore")
;
CREATE INDEX "IX1608_wellbore_development_all" ON "wellbore_development_all"
(
"prlNpdidProductionLicence"
)
;
CREATE INDEX "IX1607_wellbore_development_all" ON "wellbore_development_all"
(
"fldNpdidField"
)
;
CREATE INDEX "IX1610_wellbore_development_all" ON "wellbore_development_all"
(
"wlbDiskosWellOperator"
)
;
CREATE INDEX "IX1605_wellbore_development_all" ON "wellbore_development_all"
(
"wlbDrillingOperator"
)
;
CREATE INDEX "IX1606_wellbore_development_all" ON "wellbore_development_all"
(
"dscNpdidDiscovery"
)
;
CREATE INDEX "IX1609_wellbore_development_all" ON "wellbore_development_all"
(
"wlbNpdidWellboreReclass"
)
;
sqlite> select * from sqlite_stat1;
wellbore_development_all|IX1609_wellbore_development_all|389400 78
wellbore_development_all|IX1606_wellbore_development_all|389400 32
wellbore_development_all|IX1605_wellbore_development_all|389400 126
wellbore_development_all|IX1610_wellbore_development_all|389400 126
wellbore_development_all|IX1607_wellbore_development_all|389400 43
wellbore_development_all|IX1608_wellbore_development_all|389400 44
wellbore_development_all|wellbore_development_all_wlbNpdidWellbore_pkey|389400
1
wellbore_development_all with primary key:
sqlite> .schema
CREATE TABLE "wellbore_development_all"( wlbWellboreName TEXT,wlbWell
TEXT,wlbDrillingOperator TEXT,wlbDrillingOperatorGroup
TEXT,wlbProductionLicence TEXT,wlbPurposePlanned TEXT,wlbContent
TEXT,wlbWellType TEXT,wlbEntryDate TEXT,wlbCompletionDate TEXT,wlbField
TEXT,wlbDrillPermit TEXT,wlbDiscovery TEXT,wlbDiscoveryWellbore
TEXT,wlbKellyBushElevation REAL,wlbFinalVerticalDepth REAL,wlbTotalDepth
REAL,wlbWaterDepth REAL,wlbMainArea TEXT,wlbDrillingFacility
TEXT,wlbFacilityTypeDrilling TEXT,wlbProductionFacility
TEXT,wlbLicensingActivity TEXT,wlbMultilateral TEXT,wlbContentPlanned
TEXT,wlbEntryYear INTEGER,wlbCompletionYear
INTEGER,wlbReclassFromWellbore TEXT,wlbPlotSymbol
INTEGER,wlbGeodeticDatum TEXT,wlbNsDeg INTEGER,wlbNsMin INTEGER,wlbNsSec
REAL,wlbNsCode TEXT,wlbEwDeg INTEGER,wlbEwMin INTEGER,wlbEwSec
REAL,wlbEwCode TEXT,wlbNsDecDeg REAL,wlbEwDesDeg REAL,wlbNsUtm
REAL,wlbEwUtm REAL,wlbUtmZone INTEGER,wlbNamePart1 INTEGER,wlbNamePart2
INTEGER,wlbNamePart3 TEXT,wlbNamePart4 INTEGER,wlbNamePart5
TEXT,wlbNamePart6 TEXT,wlbFactPageUrl TEXT,wlbFactMapUrl
TEXT,wlbDiskosWellboreType TEXT,wlbDiskosWellboreParent
TEXT,wlbNpdidWellbore INTEGER PRIMARY KEY,dscNpdidDiscovery
INTEGER,fldNpdidField INTEGER,wlbWdssQcdate
TEXT,prlNpdidProductionLicence INTEGER,fclNpdidFacilityDrilling
INTEGER,fclNpdidFacilityProducing INTEGER,wlbNpdidWellboreReclass
INTEGER,wlbDiskosWellOperator TEXT,wlbDateUpdated TEXT,wlbDateUpdatedMax
TEXT,dateSyncNPD TEXT);
CREATE INDEX "IX1608_wellbore_development_all" ON "wellbore_development_all"
(
"prlNpdidProductionLicence"
)
;
CREATE INDEX "IX1607_wellbore_development_all" ON "wellbore_development_all"
(
"fldNpdidField"
)
;
CREATE INDEX "IX1610_wellbore_development_all" ON "wellbore_development_all"
(
"wlbDiskosWellOperator"
)
;
CREATE INDEX "IX1605_wellbore_development_all" ON "wellbore_development_all"
(
"wlbDrillingOperator"
)
;
CREATE INDEX "IX1606_wellbore_development_all" ON "wellbore_development_all"
(
"dscNpdidDiscovery"
)
;
CREATE INDEX "IX1609_wellbore_development_all" ON "wellbore_development_all"
(
"wlbNpdidWellboreReclass"
)
;
CREATE UNIQUE INDEX "wellbore_development_all_wlbNpdidWellbore_pkey" ON
"wellbore_development_all" ("wlbNpdidWellbore")
;
sqlite> select * from sqlite_stat1;
wellbore_development_all|wellbore_development_all_wlbNpdidWellbore_pkey|389400
1
wellbore_development_all|IX1609_wellbore_development_all|389400 78
wellbore_development_all|IX1606_wellbore_development_all|389400 32
wellbore_development_all|IX1605_wellbore_development_all|389400 126
wellbore_development_all|IX1610_wellbore_development_all|389400 126
wellbore_development_all|IX1607_wellbore_development_all|389400 43
wellbore_development_all|IX1608_wellbore_development_all|389400 44
The output of the explain for the first case(nonclustered
index-wlbNpdidWellbore is not primary key in wellbore_development_all):
sqlite> explain query plan SELECT count(qview1."wlbWellboreName") FROM
"discovery" qview2 CROSS JOIN "wellbore_development_all" qview1 WHERE
(qview1."wlbNpdidWellbore" = qview2."wlbNpdidWellbore");
0|0|0|SCAN TABLE discovery AS qview2 USING COVERING INDEX IX368_discovery
0|1|1|SEARCH TABLE wellbore_development_all AS qview1 USING INDEX
wellbore_development_all_wlbNpdidWellbore_pkey (wlbNpdidWellbore=?)
sqlite> explain SELECT count(qview1."wlbWellboreName") FROM "discovery"
qview2 CROSS JOIN "wellbore_development_all" qview1 WHERE
(qview1."wlbNpdidWellbore" = qview2."wlbNpdidWellbore");
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 23 0 00 Start at 23
1 Null 0 1 2 00 r[1..2]=NULL
2 OpenRead 2 13788 2 k(2,,) 00 root=13788
iDb=2; IX368_discovery
3 OpenRead 1 2 3 65 00 root=2 iDb=3;
wellbore_development_all
4 OpenRead 3 393308 3 k(2,,) 02 root=393308
iDb=3; wellbore_development_all_wlbNpdidWellbore_pkey
5 Rewind 2 16 3 0 00
6 Column 2 0 3 00
r[3]=discovery.wlbNpdidWellbore
7 IsNull 3 15 0 00 if
r[3]==NULL goto 15
8 Affinity 3 1 0 D 00 affinity(r[3])
9 SeekGE 3 15 3 1 00 key=r[3]
10 IdxGT 3 15 3 1 00 key=r[3]
11 Seek 3 0 1 00 Move 1 to
3.rowid
12 Column 1 0 4 00
r[4]=wellbore_development_all.wlbWellboreName
13 AggStep0 0 4 1 count(1) 01 accum=r[1]
step(r[4])
14 Next 3 10 1 00
15 Next 2 6 0 01
16 Close 2 0 0 00
17 Close 1 0 0 00
18 Close 3 0 0 00
19 AggFinal 1 1 0 count(1) 00 accum=r[1] N=1
20 Copy 1 5 0 00 r[5]=r[1]
21 ResultRow 5 1 0 00 output=r[5]
22 Halt 0 0 0 00
23 Transaction 2 0 5 0 01 usesStmtJournal=0
24 Transaction 3 0 11 0 01 usesStmtJournal=0
25 TableLock 2 2 0 discovery 00 iDb=2 root=2
write=0
26 TableLock 3 2 0 wellbore_development_all 00
iDb=3 root=2 write=0
27 Goto 0 1 0 00
The output of explain for the second case (clustered
index-wlbNpdidWellbore is primary key in wellbore_development_all):
sqlite> explain query plan SELECT count(qview1."wlbWellboreName") FROM
"discovery" qview2 CROSS JOIN "wellbore_development_all" qview1 WHERE
(qview1."wlbNpdidWellbore" = qview2."wlbNpdidWellbore");
0|0|0|SCAN TABLE discovery AS qview2 USING COVERING INDEX IX368_discovery
0|1|1|SEARCH TABLE wellbore_development_all AS qview1 USING INTEGER
PRIMARY KEY (rowid=?)
sqlite> explain SELECT count(qview1."wlbWellboreName") FROM
"discovery" qview2 CROSS JOIN "wellbore_development_all" qview1 WHERE
(qview1."wlbNpdidWellbore" = qview2."wlbNpdidWellbore");
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 17 0 00 Start at 17
1 Null 0 1 2 00 r[1..2]=NULL
2 OpenRead 2 5 3 k(2,,) 00 root=5 iDb=3;
IX368_discovery
3 OpenRead 1 3 2 65 00 root=3 iDb=2;
wellbore_development_all
4 Rewind 2 11 3 0 00
5 Column 2 0 3 00
r[3]=discovery.wlbNpdidWellbore
6 MustBeInt 3 10 0 00
7 NotExists 1 10 3 00 intkey=r[3]; pk
8 Column 1 0 4 00
r[4]=wellbore_development_all.wlbWellboreName
9 AggStep0 0 4 1 count(1) 01 accum=r[1]
step(r[4])
10 Next 2 5 0 01
11 Close 2 0 0 00
12 Close 1 0 0 00
13 AggFinal 1 1 0 count(1) 00 accum=r[1] N=1
14 Copy 1 5 0 00 r[5]=r[1]
15 ResultRow 5 1 0 00 output=r[5]
16 Halt 0 0 0 00
17 Transaction 2 0 23 0 01 usesStmtJournal=0
18 Transaction 3 0 13 0 01 usesStmtJournal=0
19 TableLock 3 3 0 discovery 00 iDb=3 root=3
write=0
20 TableLock 2 3 0 wellbore_development_all 00
iDb=2 root=3 write=0
21 Goto 0 1 0 00