Introduction
SQLite3 is a self-contained, serverless database engine that stores everything in a single file. The sqlite3 command-line tool lets you create, query, and manage SQLite databases directly from the terminal. It’s invaluable for development, debugging, and lightweight data management.
Opening a Database
# Open (or create) a database file
sqlite3 mydb.db
# Open in-memory database (lost when you exit)
sqlite3 :memory:
# Open with a specific SQL command
sqlite3 mydb.db "SELECT * FROM users LIMIT 5;"
# Open and run a SQL file
sqlite3 mydb.db < schema.sql
Dot Commands (Meta-Commands)
Dot commands control the SQLite shell itself โ they start with . and are not SQL:
# Help
sqlite> .help
# Exit
sqlite> .quit
sqlite> .exit
# Show all tables
sqlite> .tables
# Show tables matching a pattern
sqlite> .tables user%
# Show schema of all tables
sqlite> .schema
# Show schema of a specific table
sqlite> .schema users
# Show indexes
sqlite> .indexes
sqlite> .indexes users
# Show databases attached
sqlite> .databases
# Show current settings
sqlite> .show
Viewing Table Structure
sqlite> .tables
documents settings tokens
sqlite> .schema documents
CREATE TABLE documents (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
body TEXT NOT NULL
);
CREATE UNIQUE INDEX title_index ON documents(title);
sqlite> .schema tokens
CREATE TABLE tokens (
id INTEGER PRIMARY KEY,
token TEXT NOT NULL,
docs_count INT NOT NULL,
postings BLOB NOT NULL
);
CREATE UNIQUE INDEX token_index ON tokens(token);
# Describe a table (column info)
sqlite> PRAGMA table_info(users);
Output Formatting
# Column mode with headers (most readable)
sqlite> .mode column
sqlite> .headers on
sqlite> SELECT id, name, email FROM users LIMIT 5;
# Table mode (ASCII table)
sqlite> .mode table
# CSV output
sqlite> .mode csv
sqlite> .output users.csv
sqlite> SELECT * FROM users;
sqlite> .output stdout # reset to screen
# JSON output (SQLite 3.33+)
sqlite> .mode json
sqlite> SELECT * FROM users LIMIT 3;
# Markdown table
sqlite> .mode markdown
sqlite> SELECT name, email FROM users LIMIT 5;
# Box drawing characters
sqlite> .mode box
Basic SQL Operations
-- Create a table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
age INTEGER,
created DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Insert data
INSERT INTO users (name, email, age) VALUES ('Alice', '[email protected]', 30);
INSERT INTO users (name, email, age) VALUES ('Bob', '[email protected]', 25);
-- Query
SELECT * FROM users;
SELECT name, email FROM users WHERE age > 25;
SELECT * FROM users ORDER BY name ASC LIMIT 10;
-- Update
UPDATE users SET age = 31 WHERE name = 'Alice';
-- Delete
DELETE FROM users WHERE id = 2;
-- Count
SELECT COUNT(*) FROM users;
SELECT COUNT(*) FROM users WHERE age > 25;
Importing and Exporting Data
Import CSV
sqlite> .mode csv
sqlite> .import data.csv users
# With headers (skip first row)
sqlite> .import --skip 1 data.csv users
Export to CSV
sqlite> .headers on
sqlite> .mode csv
sqlite> .output export.csv
sqlite> SELECT * FROM users;
sqlite> .output stdout
Export Schema + Data (Dump)
# Full database dump (SQL format)
sqlite> .dump
# Dump to file
sqlite> .output backup.sql
sqlite> .dump
sqlite> .output stdout
# Or from shell
sqlite3 mydb.db .dump > backup.sql
# Restore from dump
sqlite3 newdb.db < backup.sql
Indexes and Performance
-- Create an index
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_age ON users(age);
-- Composite index
CREATE INDEX idx_users_name_age ON users(name, age);
-- Unique index
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
-- Drop index
DROP INDEX idx_users_age;
-- View query plan (EXPLAIN)
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = '[email protected]';
Transactions
-- Wrap multiple operations in a transaction
BEGIN TRANSACTION;
INSERT INTO orders (user_id, total) VALUES (1, 99.99);
UPDATE users SET order_count = order_count + 1 WHERE id = 1;
COMMIT;
-- Rollback on error
BEGIN TRANSACTION;
INSERT INTO orders (user_id, total) VALUES (1, 49.99);
-- Something went wrong:
ROLLBACK;
Useful PRAGMA Commands
-- Check database integrity
PRAGMA integrity_check;
-- Show foreign key enforcement status
PRAGMA foreign_keys;
-- Enable foreign keys (disabled by default!)
PRAGMA foreign_keys = ON;
-- Show page size
PRAGMA page_size;
-- Optimize database (reclaim space)
VACUUM;
-- Show table info
PRAGMA table_info(users);
-- Show index info
PRAGMA index_info(idx_users_email);
-- WAL mode (better concurrent reads)
PRAGMA journal_mode = WAL;
Working with JSON (SQLite 3.38+)
-- Store JSON
CREATE TABLE events (
id INTEGER PRIMARY KEY,
data JSON
);
INSERT INTO events (data) VALUES ('{"type": "click", "user": 42, "page": "/home"}');
-- Query JSON fields
SELECT json_extract(data, '$.type') AS event_type,
json_extract(data, '$.user') AS user_id
FROM events;
-- Filter by JSON field
SELECT * FROM events
WHERE json_extract(data, '$.type') = 'click';
Scripting with SQLite3
#!/bin/bash
# Run SQL from a shell script
DB="myapp.db"
# Execute a query and capture output
count=$(sqlite3 "$DB" "SELECT COUNT(*) FROM users;")
echo "Total users: $count"
# Run multiple statements
sqlite3 "$DB" << 'EOF'
.headers on
.mode column
SELECT id, name, email FROM users ORDER BY name LIMIT 10;
EOF
# Check if a table exists
table_exists=$(sqlite3 "$DB" "SELECT name FROM sqlite_master WHERE type='table' AND name='users';")
if [ -z "$table_exists" ]; then
echo "Table 'users' does not exist"
fi
Python Integration
import sqlite3
# Connect (creates file if not exists)
conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()
# Create table
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
''')
# Insert with parameterized query (prevents SQL injection)
cursor.execute(
"INSERT INTO users (name, email) VALUES (?, ?)",
("Alice", "[email protected]")
)
conn.commit()
# Query
cursor.execute("SELECT * FROM users WHERE name = ?", ("Alice",))
rows = cursor.fetchall()
for row in rows:
print(row)
# Use as context manager
with sqlite3.connect('mydb.db') as conn:
conn.row_factory = sqlite3.Row # access columns by name
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
for row in cursor.fetchall():
print(dict(row))
conn.close()
Useful Tools
- LiteCLI โ SQLite CLI with auto-completion and syntax highlighting
- DB Browser for SQLite โ GUI for SQLite databases
- sqlite-utils โ Python CLI tool for SQLite
- Datasette โ Publish SQLite databases as web APIs
Comments