Skip to content
SQLite Guide — Embedded Database for Applications

SQLite Guide — Embedded Database for Applications

DodaTech Updated Jun 7, 2026 11 min read

SQLite is a self-contained, serverless, zero-configuration database engine that stores the entire database as a single file, making it the most widely deployed database in the world.

What You’ll Learn

By the end of this tutorial, you’ll understand SQLite’s zero-config architecture, create and query databases using SQL, use the command-line tool for backups and restores, optimize performance with indexes, and choose between SQLite, MySQL, and PostgreSQL for your projects.

Why SQLite Matters

SQLite is everywhere — every smartphone has thousands of SQLite databases, every browser uses it for bookmarks and history, and countless desktop apps rely on it for local storage. Doda Browser uses SQLite for user bookmarks, browsing history, and cached data. Durga Antivirus Pro uses SQLite for local threat signatures on devices without network access. Learning SQLite is essential for any application developer.

SQLite Learning Path

    flowchart LR
  A[SQL Basics] --> B[MySQL]
  B --> C[SQLite]
  C --> D[PostgreSQL]
  D --> E[MongoDB]
  E --> F[Database Design]
  C --> G{You Are Here}
  style G fill:#f90,color:#fff
  
Prerequisites: Basic SQL knowledge (SELECT, INSERT, UPDATE, DELETE). No server installation needed — SQLite works out of the box on almost every system. Python users will find SQLite’s built-in module particularly useful.

What Is SQLite? (The “Why” First)

Imagine needing a database for your desktop app. With MySQL or PostgreSQL, you’d install a server, configure users, set up networking, and manage connections — overkill for a local application. SQLite flips this: there is no server. Your database is a single file on disk. Your application links against the SQLite library directly and reads/writes that file. No setup, no administration, no network.

SQLite vs MySQL vs PostgreSQL

FeatureSQLiteMySQLPostgreSQL
SetupZero (file-based)Server installServer install
ConcurrencySingle writerMany concurrentMany concurrent
StorageSingle fileMultiple filesMultiple files
NetworkNo (embedded)TCP/IPTCP/IP
User managementFile permissionsBuilt-in usersBuilt-in roles
Best forLocal/mobile appsWeb appsEnterprise apps
Max database size281 TB (practical)UnlimitedUnlimited

Creating Your First SQLite Database

The sqlite3 command-line tool is all you need:

# Create (or open) a database
sqlite3 inventory.db

# Expected output:
# SQLite version 3.43.2 2026-04-01 12:00:00
# Enter ".help" for usage hints.
-- Inside the sqlite3 prompt, create tables
CREATE TABLE products (
    product_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    description TEXT,
    price REAL NOT NULL CHECK(price > 0),
    quantity INTEGER NOT NULL DEFAULT 0,
    category TEXT NOT NULL DEFAULT 'General',
    created_at TEXT DEFAULT (datetime('now'))
);

-- Insert sample products
INSERT INTO products (name, description, price, quantity, category) VALUES
    ('Wireless Mouse', 'Ergonomic wireless mouse with USB receiver', 29.99, 150, 'Electronics'),
    ('Notebook Set', 'Pack of 5 A5 ruled notebooks', 12.50, 300, 'Stationery'),
    ('Desk Lamp', 'LED desk lamp with adjustable brightness', 39.99, 75, 'Office'),
    ('USB-C Cable', '2m braided USB-C charging cable', 9.99, 500, 'Electronics'),
    ('Whiteboard Markers', 'Set of 8 assorted colors', 7.99, 200, 'Office');

-- Verify data
SELECT * FROM products;

Output:

product_id | name              | description                                        | price | quantity | category   | created_at
1          | Wireless Mouse    | Ergonomic wireless mouse with USB receiver          | 29.99 | 150      | Electronics| 2026-06-07 10:00:00
2          | Notebook Set      | Pack of 5 A5 ruled notebooks                        | 12.50 | 300      | Stationery | 2026-06-07 10:00:00
3          | Desk Lamp         | LED desk lamp with adjustable brightness            | 39.99 | 75       | Office     | 2026-06-07 10:00:00
4          | USB-C Cable       | 2m braided USB-C charging cable                     | 9.99  | 500      | Electronics| 2026-06-07 10:00:00
5          | Whiteboard Markers| Set of 8 assorted colors                            | 7.99  | 200      | Office     | 2026-06-07 10:00:00

Querying Data

-- Products with low stock (less than 100 units)
SELECT name, quantity, price
FROM products
WHERE quantity < 100
ORDER BY quantity;

-- Output:
-- name         | quantity | price
-- Desk Lamp    | 75       | 39.99

-- Total inventory value per category
SELECT
    category,
    COUNT(*) AS product_count,
    SUM(quantity) AS total_units,
    ROUND(SUM(price * quantity), 2) AS total_value
FROM products
GROUP BY category
ORDER BY total_value DESC;

-- Output:
-- category    | product_count | total_units | total_value
-- Electronics | 2             | 650         | 9496.50
-- Office      | 2             | 275         | 2999.25
-- Stationery  | 1             | 300         | 3750.00

-- Search by name (LIKE is case-insensitive by default in SQLite)
SELECT name, price FROM products WHERE name LIKE '%cable%';

-- Output:
-- name         | price
-- USB-C Cable  | 9.99

SQLite-Specific Features

The .dump and .restore Commands

SQLite’s command line has built-in backup tools:

# Dump entire database to SQL script
sqlite3 inventory.db .dump > inventory_backup.sql

# The output file contains all CREATE TABLE + INSERT statements
# You can restore it anytime:
sqlite3 restored_inventory.db < inventory_backup.sql

# Or from within sqlite3:
sqlite3 inventory.db
sqlite> .output backup.sql
sqlite> .dump
sqlite> .exit

# Restore
sqlite3 inventory.db
sqlite> .read backup.sql

ATTACH — Working with Multiple Databases

-- Attach another database
ATTACH DATABASE 'users.db' AS users_db;

-- Query across databases
SELECT p.name, u.username
FROM products p
JOIN users_db.users u ON p.product_id = u.favorite_product_id;

The json Extension

-- SQLite has built-in JSON functions (since 3.9.0)
SELECT
    name,
    json_object('price', price, 'in_stock', json(quantity > 0)) AS product_json
FROM products
WHERE category = 'Electronics';

Output:

name           | product_json
Wireless Mouse | {"price":29.99,"in_stock":true}
USB-C Cable    | {"price":9.99,"in_stock":true}

Using SQLite with Python

import sqlite3

# Connect to database (creates if not exists)
conn = sqlite3.connect("inventory.db")
cursor = conn.cursor()

# Create a sales table
cursor.execute("""
    CREATE TABLE IF NOT EXISTS sales (
        sale_id INTEGER PRIMARY KEY AUTOINCREMENT,
        product_id INTEGER NOT NULL,
        quantity_sold INTEGER NOT NULL,
        sale_date TEXT DEFAULT (datetime('now')),
        FOREIGN KEY (product_id) REFERENCES products(product_id)
    )
""")

# Record a sale
cursor.execute(
    "INSERT INTO sales (product_id, quantity_sold) VALUES (?, ?)",
    (1, 2)
)

# Query with JOIN
cursor.execute("""
    SELECT
        p.name,
        SUM(s.quantity_sold) AS total_sold,
        ROUND(SUM(s.quantity_sold * p.price), 2) AS revenue
    FROM sales s
    JOIN products p ON s.product_id = p.product_id
    GROUP BY p.product_id
    ORDER BY revenue DESC
""")

for row in cursor.fetchall():
    print(f"{row[0]}: sold {row[1]} units, revenue ${row[2]}")

conn.commit()
conn.close()

Expected output:

Wireless Mouse: sold 2 units, revenue $59.98

SQLite Architecture

    flowchart TB
    subgraph Application
        A[App Code]
        P[Python Module]
        C[C Library]
    end
    subgraph SQLite
        I[Interface Layer]
        T[Tokenizer]
        Pa[Parser]
        Co[Code Generator]
        VM[Virtual Machine]
        B[B-Tree Engine]
        Pa2[Pager]
        OS[OS Interface]
    end
    subgraph Storage
        DB[(database.db)]
    end
    A --> P
    A --> C
    P --> I
    C --> I
    I --> T --> Pa --> Co --> VM --> B --> Pa2 --> OS
    OS --> DB
  

SQLite’s architecture is remarkably simple compared to client-server databases. There’s no network layer, no connection pooling, no authentication system — just your application talking directly to the storage engine.

Common SQLite Errors

1. database is locked

SQLite allows only one writer at a time. If another connection has an open transaction, writes fail. Fix: Use shorter transactions, implement retry logic, or switch to WAL mode.

-- Enable Write-Ahead Logging for better concurrency
PRAGMA journal_mode=WAL;
-- Output: WAL

2. attempt to write a readonly database

The database file (or its directory) is not writable by the current user. Fix: Check file permissions with ls -l database.db and the directory with ls -ld .. Ensure the user has write permission.

3. UNIQUE constraint failed

-- If product_id is PRIMARY KEY AUTOINCREMENT, this fails:
INSERT INTO products (product_id, name) VALUES (1, 'Duplicate');
-- UNIQUE constraint failed: products.product_id

Fix: Don’t specify values for AUTOINCREMENT columns, or use INSERT OR REPLACE for upserts.

4. no such table

SELECT * FROM non_existent_table;
-- Error: no such table: non_existent_table

Fix: Check spelling. List tables with .tables command. SQLite table names are case-insensitive but the spelling must be exact.

5. FOREIGN KEY constraint failed

SQLite does NOT enforce foreign keys by default! You must enable it:

PRAGMA foreign_keys = ON;

Without this, you can insert orphan records without errors.

6. data type mismatch

SQLite is flexibly typed — you can insert text into an INTEGER column. But with strict tables (SQLite 3.37+):

CREATE TABLE strict_products (
    id INTEGER PRIMARY KEY,
    price REAL NOT NULL
) STRICT;

INSERT INTO strict_products VALUES (1, 'not_a_number');
-- Error: datatype mismatch

Fix: Match the declared column types.

7. File Corruption

Power loss during a write can corrupt the database. Fix: Enable WAL mode (PRAGMA journal_mode=WAL), use PRAGMA synchronous=NORMAL, and run periodic integrity checks with PRAGMA integrity_check.

Practice Questions

1. What does “zero-configuration” mean in SQLite?

SQLite requires no server installation, no configuration files, no user management, and no network setup. You just install the library (comes pre-installed on most systems) and create a database file with a single command.

2. How does SQLite handle concurrency compared to MySQL?

SQLite allows unlimited concurrent readers but only one writer at a time. MySQL supports multiple concurrent writers with row-level locking. Use SQLite for single-user apps; use MySQL for multi-user web applications.

3. What is WAL mode in SQLite?

Write-Ahead Logging (WAL) mode improves concurrent read/write performance by allowing readers to continue reading old data while a writer writes to a separate WAL file. Enable it with PRAGMA journal_mode=WAL.

4. Challenge: Write a query that finds products with names matching a search term, using SQLite’s full-text search capabilities.

SQLite has a full-text search extension (FTS5):

-- Create a virtual FTS table
CREATE VIRTUAL TABLE products_fts USING fts5(name, description, content='products');

-- Populate from the source table
INSERT INTO products_fts SELECT name, description FROM products;

-- Search with ranking
SELECT name, rank
FROM products_fts
WHERE products_fts MATCH 'wireless OR mouse'
ORDER BY rank;

5. What is the maximum size of a SQLite database?

The default maximum page size is 4096 bytes with a maximum of 2147483646 pages, giving a theoretical maximum of about 281 TB. In practice, performance degrades beyond 100-200 GB.

Real-World Task: Build a Local Note-Taking App Database

Design the database schema for a note-taking app — similar to the local storage used by Doda Browser for bookmarks:

CREATE TABLE notes (
    note_id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    category TEXT DEFAULT 'General',
    is_pinned INTEGER DEFAULT 0,
    created_at TEXT DEFAULT (datetime('now')),
    updated_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE tags (
    tag_id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL UNIQUE
);

CREATE TABLE note_tags (
    note_id INTEGER NOT NULL,
    tag_id INTEGER NOT NULL,
    PRIMARY KEY (note_id, tag_id),
    FOREIGN KEY (note_id) REFERENCES notes(note_id) ON DELETE CASCADE,
    FOREIGN KEY (tag_id) REFERENCES tags(tag_id) ON DELETE CASCADE
);

-- Enable foreign keys for this session
PRAGMA foreign_keys = ON;

-- Insert sample data
INSERT INTO notes (title, content, category) VALUES
    ('Meeting Notes', 'Discuss Q3 roadmap and sprint planning', 'Work'),
    ('Shopping List', 'Milk, eggs, bread, coffee, chicken', 'Personal');

INSERT INTO tags (name) VALUES ('important'), ('urgent'), ('reference');

INSERT INTO note_tags (note_id, tag_id) VALUES (1, 1), (1, 2);

-- Query: Get all notes with their tags
SELECT
    n.title,
    n.category,
    GROUP_CONCAT(t.name, ', ') AS tags
FROM notes n
LEFT JOIN note_tags nt ON n.note_id = nt.note_id
LEFT JOIN tags t ON nt.tag_id = t.tag_id
GROUP BY n.note_id
ORDER BY n.updated_at DESC;

FAQ

Can SQLite handle multiple users?
Not well. SQLite allows multiple readers but only one writer at a time. For multi-user web applications, use MySQL or PostgreSQL. SQLite is designed for single-application, single-device use.
Is SQLite ACID compliant?
Yes. SQLite supports ACID transactions (Atomic, Consistent, Isolated, Durable). It uses journal files (rollback or WAL mode) to ensure data integrity even after crashes.
How do I backup a SQLite database?
Use .dump to export to SQL, copy the database file directly (safe when no writes are happening), or use the sqlite3 backup API: sqlite3 source.db ".backup backup.db".
What is the difference between SQLite and Python’s shelve module?
Both provide local storage, but SQLite uses standard SQL with powerful querying, joins, and indexes. Python’s shelve is a simple key-value store. SQLite is more capable for structured data.
Does SQLite support stored procedures?
No. SQLite is an embedded library, not a full client-server database. It focuses on simplicity. For complex logic, handle it in your application code using Python, Java, or C#.

Try It Yourself

Create the products database above and run these analytical queries:

-- Products with no sales (use LEFT JOIN)
SELECT p.name, COUNT(s.sale_id) AS times_sold
FROM products p
LEFT JOIN sales s ON p.product_id = s.product_id
GROUP BY p.product_id
HAVING times_sold = 0;

-- Average price per category
SELECT category, ROUND(AVG(price), 2) AS avg_price
FROM products
GROUP BY category;

-- Database file size
PRAGMA page_count;
PRAGMA page_size;
-- Total = page_count * page_size bytes

These patterns power local caching in DodaZIP and offline threat signature lookups in Durga Antivirus Pro — proving that even small, embedded databases can power serious applications.

What’s Next

Congratulations on completing this SQLite tutorial! Here’s where to go from here:

  • Practice daily — Consistency is more important than long study sessions
  • Build a project — Apply what you learned by building something real
  • Explore related topics — Check out other tutorials in the same category
  • Join the community — Discuss with other learners and share your progress

Remember: every expert was once a beginner. Keep coding!

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro