LEARN PYTHON | Sqlite, Queries And Parameterised Sql

Lesson objective

Create related SQLite tables, query ordered results and modify data with bound parameters, explicit transactions and deliberately closed connections.

Learn

07.4 | SQLITE, QUERIES AND PARAMETERISED SQL

01 | DATA THAT SURVIVES THE PROGRAM

A list exists in the running program’s memory. After the process ends, it is gone unless saved. A database stores organised records and supplies operations for retrieving and changing them.

SQLite stores a database in a file and runs inside your application process, so this lesson does not require a separate database server. Python supplies the sqlite3 module in its standard library.

A table has columns describing fields and rows describing records. A primary key identifies one row. SQL is the language used here to define tables, ask questions and change stored data. It is separate from Python syntax.

02 | BEGIN WITH A TABLE YOU CAN PICTURE

A reading table can contain id, title and pages. The id is an INTEGER PRIMARY KEY, title is required text and pages must be a positive integer under the Python input policy. The database CHECK below additionally rejects values whose comparison fails its condition.

SQLite ordinarily uses type affinity rather than enforcing every declared type exactly. Therefore a column declared INTEGER is not, by itself, a complete exact-int validation rule. Use Python checks for this course’s inputs and database constraints for shared invariants.

The example uses :memory: for a disposable demonstration. Replacing that with reading.sqlite3 opens or creates a persistent file at a resolved path. Do not confuse a demonstration’s memory database with the real planner’s stored file.

03 | CONNECT, CREATE, INSERT AND READ

connect returns a connection representing access to the database. execute runs a SQL statement and returns a cursor. fetchall retrieves the remaining result rows as a Python list. By default each row is a tuple.

Read the CREATE statement as a table definition. Read INSERT as creating one record. The question marks are placeholders: the tuple supplies the title and page count as values, not extra SQL.

ORDER BY makes the requested display order explicit. Without it, you must not rely on the order rows happen to appear.

from contextlib import closing
import sqlite3

with closing(sqlite3.connect(":memory:", isolation_level="DEFERRED")) as con:
    with con:
        con.execute("CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT NOT NULL, pages INTEGER NOT NULL CHECK(pages > 0))")
        con.execute("INSERT INTO books (title, pages) VALUES (?, ?)", ("Python Adventures", 120))
    rows = con.execute("SELECT id, title, pages FROM books ORDER BY id").fetchall()
    print(rows)

EXPECTED OUTPUT

[(1, 'Python Adventures', 120)]

04 | WHAT THE TWO WITH BLOCKS DO

The outer closing block closes the connection when the block exits. A sqlite3 connection’s own with block manages a transaction, but does not close the connection. A transaction groups changes that should succeed or fail together.

This example explicitly requests DEFERRED legacy transaction handling so its behaviour does not silently depend on changing defaults. The inner block commits a pending transaction on success and rolls it back when an exception escapes. Each database operation in the project follows a documented transaction policy.

DDL such as CREATE TABLE does not necessarily start an implicit transaction under this policy. To group schema statements atomically, the planner explicitly issues BEGIN first. The smaller snippet commits its inserted record; it does not claim the CREATE was automatically part of that insertion transaction.

05 | PARAMETERISE VALUES, NEVER BUILD THEM INTO SQL

Suppose a title contains an apostrophe, such as Noor’s Notes. Joining that text into SQL can break quotation structure; malicious text may change what an assembled statement means. Bound parameters keep each input a data value.

For a single parameter, write (title,) with the trailing comma to create a one-item tuple. Without the comma, parentheses alone do not turn a string into a tuple. Parameters do not add the surrounding SQL quotes yourself.

Placeholders stand for values, not table names, column names or ASC/DESC keywords. Choose such structure from a fixed allowlist when needed. Input validation enforces rules such as non-blank text; it does not replace SQL parameterisation.

PYTHON | VALUE PLACEHOLDER EXAMPLE

rows = con.execute(
    "SELECT id, title FROM books WHERE title = ? ORDER BY id",
    (title,),
).fetchall()

06 | FILTER, UPDATE AND DELETE WITH INTENTION

WHERE restricts affected rows. An UPDATE without a WHERE clause can modify every row. A DELETE without a WHERE clause can remove every row. First express exactly which primary key or rule selects the intended records.

Use the same bound-parameter technique for INSERT, SELECT, UPDATE and DELETE. rowcount can help check the number of affected rows, but its meaning belongs to the operation; do not use it as a reliable SELECT result count.

Compare the before and after report, and commit only the changes you intend. The sample works on a disposable memory database so you can experiment without deleting a real collection.

from contextlib import closing
import sqlite3

with closing(sqlite3.connect(":memory:", isolation_level="DEFERRED")) as con:
    with con:
        con.execute("CREATE TABLE tasks (id INTEGER PRIMARY KEY, title TEXT NOT NULL, done INTEGER NOT NULL CHECK(done IN (0, 1)))")
        con.executemany("INSERT INTO tasks (title, done) VALUES (?, ?)", [("Read", 0), ("Test", 0)])
        cursor = con.execute("UPDATE tasks SET done = ? WHERE id = ?", (1, 2))
        print("Updated:", cursor.rowcount)
    print(con.execute("SELECT title FROM tasks WHERE done = ? ORDER BY id", (0,)).fetchall())

EXPECTED OUTPUT

Updated: 1
[('Read',)]

07 | RELATED TABLES AND FOREIGN KEYS

A project can have many tasks. Store the project’s name once in projects, then place project_id in each task row. A foreign key describes that relationship to the project primary key.

Enable PRAGMA foreign_keys = ON for each SQLite connection before starting its transaction. Do not assume a foreign-key declaration alone has enabled enforcement in every connection.

JOIN combines related rows for a report. SELECT t.title, p.name joins each task t to project p where t.project_id = p.id. The aliases shorten column names and remove ambiguity. A normal inner join includes matching records only; LEFT JOIN can retain projects with no tasks.

08 | TRY A TITLE LOOKUP

Try an apostrophe or SQL-looking text in this fixed lookup model. Matching remains exact and the query structure remains unchanged. The browser does not open a SQLite database; run the Python examples to test real parameter binding.

09 | WHEN AN OPERATION FAILS

A uniqueness or foreign-key violation raises sqlite3.IntegrityError. Catch it outside the transaction context when you want rejection to roll back the transaction. Catching an error inside and continuing needs careful explicit transaction decisions.

Do not respond to a corrupt or unavailable database by silently replacing it with an empty one. Report the problem and retain the original file for recovery. Parameterisation prevents values changing SQL structure; it does not encrypt records or provide user authentication.

An explicit multi-change operation should begin its transaction, perform every required step and either commit all or roll back. Test that rejection leaves no partial records behind.

10 | USE A DELIBERATE FILE LOCATION

A relative database name uses the current working directory. Running from another folder could accidentally create another database with the same name. The project uses Path(__file__).resolve().parent / "planner.sqlite3" to place its file beside app.py.

Back up important data separately from source history. For this small app, close it and copy the database file before destructive experiments. Larger live applications need a proper database-aware backup approach.

CREATE TABLE IF NOT EXISTS creates missing tables; it does not migrate an old table to a new schema. A changed schema needs a deliberate migration and backup plan, rather than a misleading promise that startup will update everything.

PUT YOUR SKILLS TO WORK

Use the Section 7 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

Section 7 challenges Python course navigation

MATCH THE PYTHON TERM TO ITS MEANING

Terminology

Terminology

Database

Organised stored data with operations for retrieval and change.

Table

Rows sharing a defined set of columns.

Record

One row describing an entity or event.

Primary key

A value identifying a table row.

SQL

A language for defining, querying and changing relational data.

Connection

An object representing access to a database.

Cursor

An object executing statements and retrieving results.

Parameter

A separately bound data value supplied to a SQL placeholder.

Placeholder

A marker for a value rather than SQL structure.

Transaction

Changes grouped under a commit-or-rollback decision.

Commit

Making a transaction’s accepted changes persistent.

Rollback

Undoing a transaction’s uncommitted changes.

Foreign key

A relationship from a row to a referenced key.

JOIN

Combining related rows using a matching condition.

Constraint

A database rule restricting accepted records.

Type affinity

SQLite’s preference for storing a column’s values as particular types.

ORDER BY

A clause requesting a result ordering.

Migration

A deliberate change from an existing schema to a new one.

Questions

Questions

CHECK YOUR UNDERSTANDING

Select all correct choices. Each exact set earns one point.

1. What does ? represent in this SQLite query?
2. Which is a one-item parameter tuple?
3. Does with con close a sqlite3 connection?
4. What requests a result order?
5. What must be enabled per connection for this foreign-key policy?
6. What can an UPDATE without WHERE affect?
7. What does parameterisation achieve?
8. What does CREATE TABLE IF NOT EXISTS not do?

EXPLAIN AND COMPARE

Write your answers before revealing each sample.

1. Explain database.

2. Explain table.

3. Explain record.

4. Explain primary key.

5. Explain sql.

6. Explain connection.

7. Describe a useful test for this lesson.

8. Evaluate one limitation of the demonstrated approach.

CODE IT | YOUR INDEPENDENT CHALLENGE

Create related tables and bind user values. Test an apostrophe, a missing foreign key and rollback.

Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

Flashcards

Flashcards

Click to flip. Select the ideas you need to revisit.

0 cards selected for revision.

    Selections are kept while this page is open.

    Workbook

    Workbook

    COMING SOON

    The workbook for Sqlite, Queries And Parameterised Sql is coming soon. Complete the matching task and written questions in your exercise book.