Lesson 60 · Databases 50–60 minutes Intermediate

Python SQLite

Learn how Python applications store, retrieve, update and delete structured data using SQLite — a lightweight relational database included with Python.

0%
01

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.
02

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.

03

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
04

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.

05

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.

06

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
07

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.

08

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 identifier
  • name — product name
  • price — product price
  • stock — available quantity
09

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()
10

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.

11

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)
12

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))
13

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 = ascending
  • DESC = descending
14

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.

15

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.

16

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.

17

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.

18

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.

19

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.

20

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.

21

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.

22

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.

23

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.

24

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.

25

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

  1. Display every product.
  2. Find products costing more than 500.
  3. Display only Electronics products.
  4. Sort products from highest to lowest price.
  5. Increase the laptop stock by 5.
  6. Delete one product.
  7. 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.

26

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?

27

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 TABLE creates tables.
  • INSERT adds records.
  • SELECT retrieves records.
  • UPDATE modifies records.
  • DELETE removes 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.