SQLite
Updated: May 22, 2026Categories: Query, Backend
Printed from:
SQLite Comprehensive Cheatsheet
1. Connection and Basic Commands
CLI Connection
Bash
12345678910111213141516# Open SQLite shell
sqlite3 database.db
# Execute SQL from command line
sqlite3 database.db "SELECT * FROM users;"
# Open in read-only mode
sqlite3 -readonly database.db
# Useful dot-commands inside the shell
.tables # List tables
.schema users # Show CREATE statement
.mode box # Pretty-print results
.headers on
.timer on
Programmatic Connections
Python (sqlite3, stdlib)
Python
1234567891011121314151617import sqlite3
# Context manager handles commit/rollback on the transaction
with sqlite3.connect('database.db') as conn:
conn.row_factory = sqlite3.Row # dict-like rows
conn.execute('PRAGMA foreign_keys = ON')
# Always use parameterized queries (never f-strings)
rows = conn.execute(
'SELECT id, username FROM users WHERE created_at > ?',
('2026-01-01',),
).fetchall()
# Python 3.12+: autocommit attribute replaces the legacy
# isolation_level dance for explicit transaction control.
conn = sqlite3.connect('database.db', autocommit=False)
Node.js — node:sqlite (built-in, Node 22+)
JavaScript
1234567import { DatabaseSync } from 'node:sqlite';
const db = new DatabaseSync('database.db');
const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
const user = stmt.get(1);
db.close();
Node.js — better-sqlite3
JavaScript
1234567891011import Database from 'better-sqlite3';
const db = new Database('database.db');
const users = db.prepare('SELECT * FROM users').all();
const insert = db.prepare('INSERT INTO users (name) VALUES (?)');
const insertMany = db.transaction((rows) => {
for (const r of rows) insert.run(r.name);
});
insertMany([{ name: 'alice' }, { name: 'bob' }]);
Bun (bun:sqlite)
JavaScript
1234import { Database } from 'bun:sqlite';
const db = new Database('database.db', { strict: true });
const rows = db.query('SELECT * FROM users').all();
2. Database Operations
Create Database
Bash
123# Creates a new database file (or opens if exists)
sqlite3 newdatabase.db
Attach and Detach Databases
SQL
1234567891011-- Attach another database
ATTACH DATABASE 'secondary.db' AS secondary;
-- Query across databases
SELECT *
FROM main.users u
JOIN secondary.orders o ON u.id = o.user_id;
-- Detach database
DETACH DATABASE secondary;
3. Table Operations
Create Table
SQL
1234567CREATE TABLE users (
id INTEGER PRIMARY KEY, -- rowid alias; AUTOINCREMENT rarely needed
username TEXT NOT NULL UNIQUE,
email TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
STRICT Tables (3.37+)
SQL
1234567-- Enforces declared column types instead of relying on type affinity.
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
balance INTEGER NOT NULL,
note TEXT
) STRICT;
Allowed STRICT types: INT, INTEGER, REAL, TEXT, BLOB, ANY.
Alter Table
SQL
12345ALTER TABLE users ADD COLUMN last_login TEXT;
ALTER TABLE users RENAME COLUMN email TO email_address; -- 3.25+
ALTER TABLE users DROP COLUMN email_address; -- 3.35+
ALTER TABLE users RENAME TO app_users;
Drop Table
SQL
12DROP TABLE IF EXISTS users;
Generated Columns (3.31+)
SQL
1234567CREATE TABLE invoices (
id INTEGER PRIMARY KEY,
qty INTEGER NOT NULL,
unit REAL NOT NULL,
total REAL GENERATED ALWAYS AS (qty * unit) STORED
);
4. SQLite Data Types and Affinity
Storage Classes
NULLINTEGER(1, 2, 3, 4, 6, or 8 bytes)REAL(8-byte IEEE float)TEXT(UTF-8/16)BLOB
Type Affinity Rules (non-STRICT tables)
TEXTNUMERICINTEGERREALBLOB
In STRICT tables affinity is replaced by strict type checking — use ANY if you need the dynamic-typing behavior.
5. CRUD Operations
Insert
SQL
1234567INSERT INTO users (username, email)
VALUES ('johndoe', 'john@example.com');
INSERT INTO users (username, email) VALUES
('alice', 'alice@example.com'),
('bob', 'bob@example.com');
UPSERT (3.24+)
SQL
123456INSERT INTO users (username, email)
VALUES ('alice', 'alice@new.com')
ON CONFLICT(username) DO UPDATE
SET email = excluded.email
WHERE excluded.email <> users.email;
RETURNING (3.35+)
SQL
12345678910INSERT INTO users (username, email)
VALUES ('carol', 'carol@example.com')
RETURNING id, created_at;
UPDATE users SET last_login = datetime('now')
WHERE username = 'carol'
RETURNING id, last_login;
DELETE FROM users WHERE id = 5 RETURNING *;
Select
SQL
12345678910SELECT * FROM users;
SELECT username, email
FROM users
WHERE created_at > date('now', '-30 days');
-- Pagination (prefer keyset paging for large tables)
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 20;
SELECT * FROM users WHERE id > ? ORDER BY id LIMIT 10; -- keyset
Update / Delete
SQL
123456UPDATE users
SET last_login = datetime('now')
WHERE username = 'johndoe';
DELETE FROM users WHERE id = 5;
6. Joins, CTEs, and Window Functions
Joins
SQL
12345678SELECT u.username, o.order_id
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
SELECT u.username, o.order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
Common Table Expressions
SQL
123456789101112131415WITH recent AS (
SELECT * FROM orders WHERE created_at > date('now', '-7 days')
)
SELECT u.username, COUNT(*) AS recent_orders
FROM users u JOIN recent r ON r.user_id = u.id
GROUP BY u.id;
-- Recursive CTE
WITH RECURSIVE counter(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM counter WHERE n < 10
)
SELECT n FROM counter;
Window Functions (3.25+)
SQL
1234567SELECT
user_id,
amount,
SUM(amount) OVER (PARTITION BY user_id ORDER BY created_at) AS running_total,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders;
7. Indexes and Constraints
Indexes
SQL
12345678910CREATE INDEX idx_username ON users(username);
CREATE UNIQUE INDEX idx_email ON users(email);
-- Partial index
CREATE INDEX idx_active_users ON users(last_login)
WHERE last_login IS NOT NULL;
-- Expression index
CREATE INDEX idx_email_lower ON users(lower(email));
Constraints
SQL
1234567891011CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL CHECK (price > 0),
category TEXT DEFAULT 'Misc',
UNIQUE(name, category)
);
-- Foreign keys are OFF by default — enable per connection:
PRAGMA foreign_keys = ON;
8. Functions and Operators
String Functions
SQL
123456789101112SELECT
name || ' ' || lastname AS full_name,
substr(name, 1, 3) AS first_three,
length(name) AS name_length,
upper(name), lower(name),
instr(email, '@') AS at_pos,
replace(email, '@', ' at ') AS masked
FROM users;
-- printf / format (alias added in 3.38) for formatted strings
SELECT format('User #%d: %s', id, username) FROM users;
Math Functions (3.35+)
SQL
12SELECT pi(), sqrt(2), pow(2, 10), ln(10), log10(1000), ceil(1.2), floor(1.8);
Date and Time
SQL
123456SELECT datetime('now'); -- UTC by default
SELECT datetime('now', 'localtime');
SELECT date('now', '+1 month');
SELECT unixepoch('now'); -- 3.38+
SELECT strftime('%Y-%m', created_at) AS month FROM orders;
JSON / JSONB (JSONB storage added in 3.45)
SQL
123456789101112-- Extract values
SELECT json_extract(data, '$.email') FROM users;
SELECT data ->> '$.email' FROM users; -- text result
SELECT data -> '$.tags' FROM users; -- JSON result
-- Build JSON
SELECT json_object('id', id, 'name', username) FROM users;
-- JSONB (binary representation, faster to query)
INSERT INTO users (data) VALUES (jsonb('{"email":"a@b.c","tags":["x","y"]}'));
SELECT jsonb_extract(data, '$.tags[0]') FROM users;
9. SQLite-Specific Features
Full-Text Search (FTS5)
SQL
12345678910CREATE VIRTUAL TABLE docs USING fts5(title, content, tokenize = 'porter unicode61');
INSERT INTO docs(title, content) VALUES ('SQLite', 'A small, fast database engine');
-- Match query + ranking + snippet/highlight
SELECT title, snippet(docs, 1, '<b>', '</b>', '…', 10) AS preview
FROM docs
WHERE docs MATCH 'sqlite OR database'
ORDER BY rank;
Triggers
SQL
123456789CREATE TRIGGER update_timestamp
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
UPDATE users
SET updated_at = datetime('now')
WHERE id = NEW.id;
END;
Views
SQL
123CREATE VIEW active_users AS
SELECT * FROM users WHERE last_login > date('now', '-30 days');
10. Pragma Commands
SQL
12345678910111213141516-- Concurrency / durability
PRAGMA journal_mode = WAL; -- Recommended for most apps
PRAGMA synchronous = NORMAL; -- Safe and fast with WAL
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000; -- ms; avoids SQLITE_BUSY in concurrent writers
-- Memory / I/O
PRAGMA cache_size = -20000; -- ~20 MiB
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 268435456; -- 256 MiB memory-mapped I/O
-- Maintenance
PRAGMA optimize; -- Run periodically (e.g. on close)
PRAGMA integrity_check;
PRAGMA wal_checkpoint(TRUNCATE);
11. Performance Optimization
Recommended Startup Pragmas
SQL
1234567PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
PRAGMA cache_size = -20000;
PRAGMA temp_store = MEMORY;
Batch Transactions
SQL
1234BEGIN IMMEDIATE; -- acquire write lock up front
-- many INSERT/UPDATE/DELETE
COMMIT;
Query Plans
SQL
123456EXPLAIN QUERY PLAN
SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC;
ANALYZE; -- refresh statistics
PRAGMA optimize; -- automatic, lightweight ANALYZE
12. Concurrency Patterns
SQLite supports many concurrent readers but only one writer at a time. In WAL mode readers do not block the writer (or each other).
Python
123456789101112131415# Python: short, retried write transactions
import sqlite3, time
def write_with_retry(conn, fn, retries=5):
for attempt in range(retries):
try:
with conn: # BEGIN/COMMIT/ROLLBACK
conn.execute('BEGIN IMMEDIATE')
return fn(conn)
except sqlite3.OperationalError as e:
if 'locked' in str(e) and attempt < retries - 1:
time.sleep(0.05 * (2 ** attempt))
continue
raise
Per-thread connections are preferred over sharing one connection across threads. Most drivers (better-sqlite3, bun:sqlite, node:sqlite) are synchronous and single-connection-per-thread.
13. Backup and Migration
Online Backup (preferred — safe with live writers)
Bash
123456# CLI dot-command — uses the SQLite Online Backup API
sqlite3 source.db ".backup 'destination.db'"
# Or, with the live DB open, use VACUUM INTO (3.27+) for a consistent snapshot
sqlite3 source.db "VACUUM INTO 'snapshot.db';"
Python
12345678# Python: programmatic online backup
import sqlite3
src = sqlite3.connect('source.db')
dst = sqlite3.connect('destination.db')
with dst:
src.backup(dst)
src.close(); dst.close()
Note: SQLite does not have a
BACKUP DATABASE TO …SQL statement — that syntax belongs to other engines. Use.backup,VACUUM INTO, or the C/SDK backup API.
Schema Migrations
SQL
1234-- Use user_version to track schema state
PRAGMA user_version; -- read
PRAGMA user_version = 7; -- write after applying migration #7
Best Practices
- Enable WAL mode and set
synchronous = NORMALfor most apps. - Always
PRAGMA foreign_keys = ON(per connection). - Use parameterized / prepared statements — never string-interpolate values.
- Wrap batch writes in a single
BEGIN IMMEDIATE … COMMITtransaction. - Prefer
STRICTtables for new schemas to catch type mistakes early. - Create indexes that match your
WHERE/ORDER BY/JOINcolumns; verify withEXPLAIN QUERY PLAN. - Run
PRAGMA optimizeon connection close andANALYZEafter large data changes. - Back up with the Online Backup API or
VACUUM INTO, never by copying a live.dbfile. - Set a
busy_timeoutso concurrent writers retry instead of failing immediately. - Use
RETURNINGto avoid a follow-upSELECTafterINSERT/UPDATE/DELETE.
Recommended Tools
- DB Browser for SQLite — cross-platform GUI
- SQLiteStudio — free GUI with schema designer
sqlite3CLI — bundled with SQLite; supports.dump,.backup,.import,.expert- VS Code: SQLite Viewer / SQLTools extensions
- Litestream / LiteFS — streaming replication and HA for SQLite
- sqlc, Drizzle, Prisma, SQLAlchemy — typed query / ORM layers with SQLite support
Performance Tips for Embedded Use
- Keep transactions short and use
BEGIN IMMEDIATEfor writes. - Reuse prepared statements; don't re-parse SQL in hot loops.
- Minimize disk I/O — large
cache_size,mmap_size, andtemp_store = MEMORYhelp. - Add the right indexes; remove unused ones (they slow writes).
- Periodically
PRAGMA optimize;VACUUMonly when needed (it rewrites the whole DB). - Profile with
EXPLAIN QUERY PLANandsqlite3_stmt_status/ driver-level timing.
Continue Learning
Discover more cheatsheets to boost your productivity