SQLite basics.
Why SQLite
Embedded, single-file, zero-config. Ships with most languages. Great for: local apps, edge, tests, small/medium prod.
CLI
sqlite3 mydb.sqlite
.help
.tables
.schema users
.mode column
.headers on
.quit
sqlite3 mydb "SELECT * FROM users"
sqlite3 mydb < script.sql
Create table
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_email ON users(email);
INTEGER PRIMARY KEY
Alias for ROWID; auto-incremented. Faster than separate AUTOINCREMENT.
Types (dynamic)
SQLite has no enforced types by default. Types:
- INTEGER
- REAL
- TEXT
- BLOB
- NUMERIC
Use:
- INTEGER for ints.
- TEXT for strings, dates, JSON.
- REAL for floats.
- BLOB for binary.
STRICT tables (3.37+)
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL
) STRICT;
Enforces types.
Insert / select / update / delete
INSERT INTO users (email, name) VALUES ('[email protected]', 'Alice');
INSERT INTO users (email, name) VALUES ('[email protected]', 'A') ON CONFLICT (email) DO UPDATE SET name = excluded.name;
SELECT * FROM users WHERE name LIKE 'A%';
SELECT count(*) FROM users;
UPDATE users SET name = 'B' WHERE id = 1;
DELETE FROM users WHERE id = 1;
RETURNING (3.35+)
INSERT INTO users (email, name) VALUES ('a@b', 'A') RETURNING id;
UPDATE users SET name = 'B' WHERE id = 1 RETURNING *;
JSON
INSERT INTO posts (data) VALUES (json('{"title":"x","tags":["a","b"]}'));
SELECT json_extract(data, '$.title') FROM posts;
SELECT data->>'$.title' FROM posts; -- arrow operator
SELECT * FROM posts WHERE json_extract(data, '$.tags[0]') = 'a';
Date / time
SELECT date('now');
SELECT datetime('now');
SELECT strftime('%Y-%m-%d', 'now');
SELECT date('now', '-7 days');
SELECT datetime('now', 'localtime');
Stored as TEXT in ISO 8601.
Transactions
BEGIN;
INSERT ...;
COMMIT;
-- Or savepoint
SAVEPOINT s1;
ROLLBACK TO s1;
Foreign keys (off by default!)
PRAGMA foreign_keys = ON;
CREATE TABLE posts (
id INTEGER PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title TEXT
);
Must enable per connection.
Useful PRAGMAs
PRAGMA journal_mode = WAL; -- highly recommended
PRAGMA synchronous = NORMAL; -- with WAL
PRAGMA cache_size = -64000; -- ~64MB cache
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
Backup
sqlite3 mydb.sqlite ".backup backup.sqlite"
Online; safe to run while DB in use.
Common mistakes
- Foreign keys off by default.
- Reading without WAL → reader blocks writer.
- Multiple writers (one writer at a time).
- AUTOINCREMENT when not needed (slower than ROWID alias).
- Strict types not used.
Read this next
If you want my SQLite setup, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .