The presentation covers several topics related to performance in SQLite and I attempt to provide you with some tools to use when you're trying to optimize data persistence in your app.
app might require that you should put some thought into how queries and statements are exectued. 2. How to actually understand the ways SQLite will execute them. 3. Bonus: Sysadmins hate him! Use this one weird trick to pull your data from the cloud and access it in two steps! @jasonwyatt
down from the cloud. — Storing large amounts of sensor data generated by the device might be important to your app. — You'll likely need to be able to search that data quickly, often in response to user input. @jasonwyatt
it, there are some important things you should learn about: — Prepared Statements and the Virtual Machine — How Indexes Work — Transactions — Databases are kept in a single file @jasonwyatt
compiled into a bytecode program called a prepared statement — Prepared statements can be referenced and reused — Input parameters and values from the database are kept in the VDBE's registers — ? tokens are actually parameters for the prepared statement (like command line arguments for a program) @jasonwyatt
how SQLite will go about rounding up your data. — When your queries are slow — When you are suspicious that complex queries could be optimized — Evaluate the need for updating/creating indexes @jasonwyatt
band_name, a.title AS album_title FROM bands AS b JOIN albums AS a ON a.band_id = b.id WHERE b.location = 'Berlin' ORDER BY b.name, a.title; selectid order from detail ---------- ---------- ---------- ------------------------ 0 0 0 SCAN TABLE bands AS b 0 1 1 SEARCH TABLE albums A... 0 0 0 USE TEMP B-TREE FOR O... @jasonwyatt
band_name, a.title AS album_title FROM bands AS b JOIN albums AS a ON a.band_id = b.id WHERE b.location = 'Berlin' ORDER BY b.name, a.title; selectid order from detail ---------- ---------- ---------- ------------------------ 0 0 0 SCAN TABLE bands AS b 0 1 1 SEARCH TABLE albums A... 0 0 0 USE TEMP B-TREE FOR O... @jasonwyatt
table, you're essentially providing a faster way for SQLite to find your data. — PRIMARY KEY is kind of like an index, provides ordering for the table itself. If you don't specify one, you will be provided with rowid. — You can use CREATE INDEX to make more targeted indexes, including indexes on multiple columns. — Indexes allow SQLite to use binary search to find your data when you're looking for records based on something other than the PRIMARY KEY. @jasonwyatt
NULL, selectid order from detail `name` TEXT NOT NULL, ---------- ---------- ---------- --------------------- `location` TEXT 0 0 0 SCAN TABLE bands AS b ); 0 1 1 SEARCH TABLE albums A 0 0 0 USE TEMP B-TREE FOR O EXPLAIN QUERY PLAN SELECT a.id AS album_id, b.name AS band_name, a.title AS album_title FROM bands AS b JOIN albums AS a ON a.band_id = b.id WHERE b.location = 'Berlin' ORDER BY b.name, a.title; @jasonwyatt
NULL, selectid order from detail `name` TEXT NOT NULL, ---------- ---------- ---------- --------------------- `location` TEXT 0 0 0 SCAN TABLE bands AS b ); 0 1 1 SEARCH TABLE albums A 0 0 0 USE TEMP B-TREE FOR O EXPLAIN QUERY PLAN SELECT a.id AS album_id, b.name AS band_name, a.title AS album_title FROM bands AS b JOIN albums AS a ON a.band_id = b.id WHERE b.location = 'Berlin' ORDER BY b.name, a.title; @jasonwyatt
name location Scan Location → 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool Scan Location → 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis Scan Location → 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles Scan Location → 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles Scan Location → 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London Scan Location → 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney Scan Location → 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin Scan Location → 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm Scan Location → ... ... ... ... @jasonwyatt
rowid Austin Explosions in the Sky 11 Austin Spoon 10 Berlin Rammstein 7 Berlin Real McCoy 9 Chicago Chicago 12 Denver The Lumineers 13 Liverpool The Beatles 1 ... ... ... Stockholm ABBA 8 Sydney AC/DC 6 @jasonwyatt
= 'Berlin'; selectid order from detail ---------- ---------- ---------- -------------------------------------------------------- 0 0 0 SCAN TABLE bands After CREATE INDEX band_locname ON bands (`location`, `name` ASC); EXPLAIN QUERY PLAN SELECT * FROM bands WHERE location = 'Berlin'; selectid order from detail ---------- ---------- ---------- -------------------------------------------------------- 0 0 0 SEARCH TABLE bands USING INDEX band_locname (location=?) @jasonwyatt
rowid Austin Explosions in the Sky 11 Austin Spoon 10 Berlin Rammstein 7 Berlin Real McCoy 9 Chicago Chicago 12 Denver The Lumineers 13 Liverpool The Beatles 1 ... ... ... Stockholm ABBA 8 Sydney AC/DC 6 @jasonwyatt
rowid Austin Explosions in the Sky 11 Austin Spoon 10 Berlin Rammstein 7 Berlin Real McCoy 9 Chicago Chicago 12 Denver The Lumineers 13 Liverpool The Beatles 1 ... ... ... Stockholm ABBA 8 Sydney AC/DC 6 @jasonwyatt
rowid Austin Explosions in the Sky 11 Austin Spoon 10 Berlin Rammstein 7 Berlin Real McCoy 9 Chicago Chicago 12 Denver The Lumineers 13 Liverpool The Beatles 1 ... ... ... Stockholm ABBA 8 Sydney AC/DC 6 @jasonwyatt
rowid Austin Explosions in the Sky 11 Austin Spoon 10 Berlin Rammstein 7 Berlin Real McCoy 9 Chicago Chicago 12 Denver The Lumineers 13 Liverpool The Beatles 1 ... ... ... Stockholm ABBA 8 Sydney AC/DC 6 @jasonwyatt
record using band_locname at rowid 7 rowid id name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm Using Index → ... ... ... ... @jasonwyatt
record using band_locname at rowid 7 rowid id name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles Using Index → 5 5 Led Zeppelin London 6 6 AC/DC Sydney 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
record using band_locname at rowid 7 rowid id name location 1 1 The Beatles Liverpool 2 2 Elvis Presley Memphis 3 3 Michael Jackson Los Angeles 4 4 Madonna Los Angeles 5 5 Led Zeppelin London 6 6 AC/DC Sydney Using Index → 7 7 Rammstein Berlin 8 8 ABBA Stockholm ... ... ... ... @jasonwyatt
columns you are selecting, it's called a "covering index". — SQLite is smart enough to skip the binary search of the actual table data and just returns information straight from the index. @jasonwyatt
a copy of the data it indexes. — Not as useful without high "cardinality". (lots of different values) — INSERT / UPDATE / DELETE will take a little longer. @jasonwyatt
NULL, `band_id` INTEGER NOT NULL, `title` TEXT NOT NULL ); CREATE TABLE bands ( `id` INTEGER PRIMARY KEY NOT NULL, `name` TEXT NOT NULL, `location` TEXT ); CREATE INDEX album_bandid ON albums (`band_id`); CREATE INDEX band_locname ON bands (`location`, `name`); @jasonwyatt
NULL, `band_id` INTEGER NOT NULL, `title` TEXT NOT NULL ); CREATE TABLE bands ( `id` INTEGER PRIMARY KEY NOT NULL, `name` TEXT NOT NULL, `location` TEXT ); CREATE INDEX album_bandid ON albums (`band_id`); CREATE INDEX band_locname ON bands (`location`, `name`); @jasonwyatt
band_name, a.title AS album_title FROM bands AS b JOIN albums AS a ON a.band_id = b.id WHERE b.location = 'Berlin' ORDER BY b.name, a.title; @jasonwyatt
0 0 0 SCAN TABLE bands AS b 0 1 1 SEARCH TABLE albums AS a USING AUTOMATIC COVERING INDEX (band_id=?) 0 0 0 USE TEMP B-TREE FOR ORDER BY After selectid order from detail ---------- ---------- ---------- ---------------------------------------------------------------------- 0 0 0 SEARCH TABLE bands AS b USING COVERING INDEX band_locname (location=?) 0 1 1 SEARCH TABLE albums AS a USING INDEX album_bandid (band_id=?) 0 0 0 USE TEMP B-TREE FOR RIGHT PART OF ORDER BY @jasonwyatt
0 0 0 SCAN TABLE bands AS b 0 1 1 SEARCH TABLE albums AS a USING AUTOMATIC COVERING INDEX (band_id=?) 0 0 0 USE TEMP B-TREE FOR ORDER BY After selectid order from detail ---------- ---------- ---------- ---------------------------------------------------------------------- 0 0 0 SEARCH TABLE bands AS b USING COVERING INDEX band_locname (location=?) 0 1 1 SEARCH TABLE albums AS a USING INDEX album_bandid (band_id=?) 0 0 0 USE TEMP B-TREE FOR RIGHT PART OF ORDER BY @jasonwyatt
0 0 0 SCAN TABLE bands AS b 0 1 1 SEARCH TABLE albums AS a USING AUTOMATIC COVERING INDEX (band_id=?) 0 0 0 USE TEMP B-TREE FOR ORDER BY After selectid order from detail ---------- ---------- ---------- ---------------------------------------------------------------------- 0 0 0 SEARCH TABLE bands AS b USING COVERING INDEX band_locname (location=?) 0 1 1 SEARCH TABLE albums AS a USING INDEX album_bandid (band_id=?) 0 0 0 USE TEMP B-TREE FOR RIGHT PART OF ORDER BY @jasonwyatt
method you will call from onStart() — Use cursor.getCount() in getItemCount() — Move the cusor back and forth with cursor.moveToPosition(pos) when fetching data for onBindViewHolder() — Don't forget to tear down the cursor with a method you can call from onStop() @jasonwyatt
a transaction. Any command that changes the database will automatically start a transaction if one is not already in effect. Automatically started transactions are committed when the last query finishes. — https://sqlite.org/lang_transaction.html @jasonwyatt
bands (id, name, location) VALUES (?, ?, ?), (?, ?, ?), ...", ...); Warning! SQLite limits you to 999 variables per statement. From sqlite3.c: /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif @jasonwyatt
as big of an improvement when compared with going from not-wrapping inserts in a transaction, but still good. — Beware of the max variable number restriction. — Some older versions of Android (and thus: older versions of SQLite) are limited to the number of records that can be inserted at once. @jasonwyatt
more complex. — Api versioning needs to be in lock-step with the app's versioning. — If you want to allow the user to change the data, consider a second database. — "Joining" data between two separate SQLite databases needs to be done in code. — This method of fetching and opening a database isn't yet possible with Room. @jasonwyatt
them happy and try to make it so new users' experiences don't degrade as they grow into your app. Get curious with EXPLAIN and EXPLAIN QUERY PlAN and profile your code. @jasonwyatt