Python SQLite
Learn how Python applications store, retrieve, update and delete structured data using SQLite — a lightweight relational database included with Python.
Learning Objectives
In earlier lessons, you worked with variables, lists, dictionaries and files. Databases provide a more structured and powerful way to store application data.
You will learn how to:
- Understand databases and tables.
- Understand SQLite.
- Connect Python to a SQLite database.
- Create database tables.
- Insert records.
- Read records using SQL.
- Filter and sort database results.
- Update and delete records.
- Use parameterized queries safely.
- Understand transactions and commits.
- Work with database constraints.
- Handle database errors.
- Build a small database-driven application.
What Is a Database?
A database is an organized system for storing and managing information.
Instead of keeping application data scattered across many text files, a database can organize information into structured tables.
For example, an ecommerce application might store:
- Customers
- Products
- Orders
- Payments
- Inventory
Database
│
├── customers
├── products
├── orders
└── payments
Key Idea
A database is designed to make storing, searching and managing structured information efficient.
What Is SQLite?
SQLite is a lightweight relational database engine.
Unlike database systems that normally run as separate server processes, SQLite stores the database in a file.
Python Application
↓
sqlite3 module
↓
SQLite database
↓
app.db file
SQLite supports SQL, tables, relationships, indexes, constraints and transactions.
Where SQLite Is Useful
- Small applications
- Desktop software
- Prototypes
- Learning projects
- Local data storage
- Embedded applications
Why Use SQLite?
SQLite is especially convenient for Python learners
because Python provides built-in support through the
sqlite3 module.
You generally do not need to install a separate SQLite server just to begin working with a database.
Advantages
- Lightweight
- Simple to set up
- Serverless
- File-based
- Supports SQL
- Included with Python's standard library
Important Limitation
SQLite is not automatically the best choice for every large, highly concurrent application. Larger systems may use database servers such as PostgreSQL or MySQL.
The sqlite3 Module
Python provides SQLite functionality through the
built-in sqlite3 module.
import sqlite3
No pip installation is normally required.
The module provides functions and classes for connecting to SQLite databases and executing SQL statements.
Connecting to a Database
Use sqlite3.connect() to connect to a
database.
import sqlite3
connection = sqlite3.connect("shop.db")
print("Database connected!")
If the database file does not exist, SQLite can create it.
You should close the connection when you are finished.
connection.close()
Connection Lifecycle
Connect
↓
Execute SQL
↓
Commit changes
↓
Close connection
Database Cursors
A cursor is used to execute SQL statements and retrieve results.
import sqlite3
connection = sqlite3.connect("shop.db")
cursor = connection.cursor()
You can then execute SQL:
cursor.execute("SELECT 1")
print(cursor.fetchone())
The cursor acts as an interface between your Python program and the database.
Creating a Table
SQL uses CREATE TABLE to create tables.
import sqlite3
connection = sqlite3.connect("shop.db")
cursor = connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL,
stock INTEGER DEFAULT 0
)
""")
connection.commit()
connection.close()
The table contains four columns:
id— unique product identifiername— product nameprice— product pricestock— available quantity
Inserting Data
Use INSERT INTO to add records.
cursor.execute("""
INSERT INTO products (name, price, stock)
VALUES ('Laptop', 1200, 10)
""")
connection.commit()
You can insert multiple records efficiently using
executemany().
products = [
("Laptop", 1200, 10),
("Phone", 800, 20),
("Tablet", 500, 15)
]
cursor.executemany("""
INSERT INTO products (name, price, stock)
VALUES (?, ?, ?)
""", products)
connection.commit()
Parameterized Queries
Never construct SQL queries by directly concatenating untrusted user input.
Use placeholders instead.
name = "Laptop"
price = 1200
stock = 10
cursor.execute("""
INSERT INTO products (name, price, stock)
VALUES (?, ?, ?)
""", (name, price, stock))
Why This Matters
Parameterized queries separate SQL instructions from data and help protect applications against SQL injection.
Avoid This
name = input("Product: ")
query = "SELECT * FROM products WHERE name = '" + name + "'"
cursor.execute(query)
Build SQL using parameters rather than inserting untrusted input directly into the SQL string.
Reading Data
Use SELECT to retrieve records.
cursor.execute("SELECT * FROM products")
rows = cursor.fetchall()
for row in rows:
print(row)
A returned row is commonly represented as a tuple.
(1, 'Laptop', 1200.0, 10)
You can select specific columns:
cursor.execute(
"SELECT name, price FROM products"
)
for row in cursor.fetchall():
print(row)
Filtering with WHERE
The WHERE clause filters records.
cursor.execute(
"SELECT * FROM products WHERE price > ?",
(500,)
)
products = cursor.fetchall()
for product in products:
print(product)
You can combine conditions:
cursor.execute("""
SELECT *
FROM products
WHERE price > ? AND stock > ?
""", (500, 5))
Sorting Results
SQL uses ORDER BY to sort records.
cursor.execute("""
SELECT name, price
FROM products
ORDER BY price ASC
""")
For descending order:
cursor.execute("""
SELECT name, price
FROM products
ORDER BY price DESC
""")
Remember
ASC= ascendingDESC= descending
Updating Data
Use UPDATE to modify existing records.
cursor.execute("""
UPDATE products
SET price = ?
WHERE name = ?
""", (1100, "Laptop"))
connection.commit()
The WHERE condition is extremely important.
Be Careful!
UPDATE products
SET price = 100
Without a WHERE clause, this can update
every row in the table.
Deleting Data
Use DELETE to remove records.
cursor.execute("""
DELETE FROM products
WHERE name = ?
""", ("Tablet",))
connection.commit()
Important Warning
Always think carefully before executing
DELETE. A missing WHERE
clause can remove every row from the table.
commit() and Transactions
Database changes are handled through transactions.
After operations such as
INSERT, UPDATE and
DELETE, you normally commit the changes.
connection.commit()
If something goes wrong before committing, a transaction can be rolled back.
try:
cursor.execute("""
UPDATE products
SET stock = stock - 1
WHERE id = ?
""", (1,))
connection.commit()
except sqlite3.Error:
connection.rollback()
Transaction Principle
Either the intended group of changes succeeds, or you can roll back the transaction instead of leaving the database in an unintended intermediate state.
fetchone() and fetchall()
After a SELECT, you can retrieve results in
different ways.
fetchone()
cursor.execute(
"SELECT * FROM products"
)
product = cursor.fetchone()
print(product)
fetchone() returns one row at a time.
fetchall()
cursor.execute(
"SELECT * FROM products"
)
products = cursor.fetchall()
for product in products:
print(product)
fetchall() retrieves all remaining rows.
Practical Consideration
For very large result sets, loading every row into memory at once may not be ideal. Iterating over the cursor can be more appropriate.
Working with sqlite3.Row
By default, SQLite rows behave like tuples. You can
configure the connection to return
sqlite3.Row objects.
import sqlite3
connection = sqlite3.connect("shop.db")
connection.row_factory = sqlite3.Row
cursor = connection.cursor()
cursor.execute(
"SELECT * FROM products"
)
product = cursor.fetchone()
print(product["name"])
print(product["price"])
This can make database code easier to read because you can access columns by name.
Database Constraints
Constraints enforce rules on data.
PRIMARY KEY
Uniquely identifies a record.
NOT NULL
Prevents a column from containing NULL.
UNIQUE
Prevents duplicate values.
DEFAULT
Provides a default value.
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
active INTEGER DEFAULT 1
)
Why Constraints Matter
Database rules provide another layer of protection against invalid or inconsistent data.
Relationships & JOINs
Relational databases often contain multiple related tables.
customers
│
│ customer_id
↓
orders
For example:
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER,
amount REAL,
FOREIGN KEY (customer_id)
REFERENCES customers(id)
)
A JOIN can combine related information.
SELECT
customers.name,
orders.amount
FROM customers
JOIN orders
ON customers.id = orders.customer_id
Why JOINs Matter
Instead of duplicating customer information in every order record, related tables can store the information separately and connect through keys.
Handling Database Errors
Database operations can fail for many reasons.
import sqlite3
try:
connection = sqlite3.connect("shop.db")
cursor = connection.cursor()
cursor.execute("""
INSERT INTO products (name, price)
VALUES (?, ?)
""", ("Laptop", 1200))
connection.commit()
except sqlite3.Error as error:
print("Database error:", error)
finally:
if connection:
connection.close()
The sqlite3.Error family can be used to
catch SQLite-related exceptions.
Good Practice
Do not silently ignore database exceptions. Log or report useful information and decide whether the operation should be retried, rolled back or rejected.
Database Best Practices
1. Use Parameterized Queries
Keep SQL and user-provided data separate.
2. Use Transactions
Commit related changes together when appropriate.
3. Close Connections
Release database resources when you are finished.
4. Validate Application Input
Database constraints are important, but application validation is also useful.
5. Design Tables Carefully
Think about identifiers, relationships, constraints and indexes before the application becomes large.
6. Back Up Important Data
A database file is still data that can be lost or corrupted. Important applications need an appropriate backup strategy.
Common SQLite Mistakes
1. Forgetting commit()
Changes may not be persisted as intended if the transaction is not committed.
2. Forgetting to Close Connections
Connections should be managed carefully.
3. Building SQL with String Concatenation
Use parameterized queries instead.
4. Forgetting WHERE
An UPDATE or DELETE without an appropriate condition can affect every record.
5. Ignoring Database Design
Poorly designed tables can create duplicate, inconsistent or difficult-to-query data.
6. Loading Huge Results Unnecessarily
Consider how much data you actually need before using
fetchall().
Database Rule
Treat database changes as important operations. Always know which records your SQL statement can affect before executing it.
Practice Exercises
Exercise 1 — Create a Database
Create a SQLite database named
students.db.
Create a students table containing:
- id
- name
- age
- grade
Exercise 2 — Insert Students
Insert at least five students using
executemany().
Exercise 3 — Query Students
Write SQL queries to:
- Display all students.
- Display students older than 18.
- Sort students by age.
- Find students with grade "A".
Exercise 4 — Update & Delete
Update one student's grade and delete another student's record.
Verify the results with a SELECT query.
Mini Challenge — Ecommerce Product Database
Build a small SQLite database for an ecommerce application.
Your Mission
Create a database named
store.db with a
products table.
The table should contain:
- id
- name
- category
- price
- stock
Starter Code
import sqlite3
connection = sqlite3.connect("store.db")
cursor = connection.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
category TEXT NOT NULL,
price REAL NOT NULL,
stock INTEGER DEFAULT 0
)
""")
products = [
("Laptop", "Electronics", 1200, 10),
("Phone", "Electronics", 800, 15),
("Desk", "Furniture", 300, 5),
("Chair", "Furniture", 150, 12)
]
cursor.executemany("""
INSERT INTO products
(name, category, price, stock)
VALUES (?, ?, ?, ?)
""", products)
connection.commit()
Challenge Tasks
- Display every product.
- Find products costing more than 500.
- Display only Electronics products.
- Sort products from highest to lowest price.
- Increase the laptop stock by 5.
- Delete one product.
- Calculate the total inventory value.
Inventory Value Formula
inventory_value = price * stock
Professional Extension
Add an orders table and connect it to
the products table using a foreign key. Then
experiment with a JOIN to display order information
together with product information.
Knowledge Check
1. What is SQLite?
2. Which Python module provides SQLite support?
3. What is a cursor used for?
4. Which SQL command retrieves data?
5. Why use parameterized queries?
6. What does commit() do?
7. What does fetchall() return?
8. What is the danger of UPDATE without WHERE?
9. What does a PRIMARY KEY provide?
10. What does JOIN help you do?
Lesson Summary
SQLite gives Python applications a practical way to store structured data using a relational database.
What You Learned
- A database stores organized application data.
- SQLite is a lightweight relational database engine.
-
Python provides SQLite support through
sqlite3. - Connections allow Python to communicate with the database.
- Cursors execute SQL statements and retrieve results.
-
CREATE TABLEcreates tables. -
INSERTadds records. -
SELECTretrieves records. -
UPDATEmodifies records. -
DELETEremoves records. - Parameterized queries help protect against SQL injection.
-
commit()saves transaction changes. - Transactions can be rolled back when necessary.
- Constraints help maintain data integrity.
- JOINs allow related information to be combined.
- Good database design is essential for reliable applications.
You have now moved from storing data in files to working with a real relational database. In the next lesson, you will learn how Python communicates with the outside world using HTTP requests.
Ready to Continue?
You have completed the Python SQLite lesson.