Python MySQL Tutorial

Connect Python to MySQL, run parameterized SELECT, INSERT and UPDATE statements, and manage transactions and connections safely.

Lesson 69 of 107Advanced12 min read

To use MySQL from Python, you install a database driver and use it to open a connection, send SQL through a cursor, and read the results as Python tuples or dictionaries. This lesson uses Oracle's official driver, MySQL Connector/Python:

Terminal
python -m pip install mysql-connector-python
Python
import mysql.connector
connection = mysql.connector.connect(host="localhost", user="library", password="...", database="library")
cursor = connection.cursor()
cursor.execute("SELECT name FROM members WHERE member_id = %s", (7,))
print(cursor.fetchone())

The sections below cover set-up, connecting with configuration instead of hard-coded passwords, parameterized queries that prevent SQL injection, and committing or rolling back changes. The examples manage loans for a community library. You need a running MySQL server, such as a local installation or a Docker container, and some familiarity with SQL; the SQL course covers the queries themselves.

Driver setup

A driver is the Python package that speaks MySQL's network protocol. Install it into your project's virtual environment:

Terminal
python -m pip install mysql-connector-python

The package installs the client library only. It doesn't install or start a MySQL server; that's a separate piece of software, for example MySQL Community Server, a managed cloud database, or docker run -e MYSQL_ROOT_PASSWORD=... -p 3306:3306 mysql:8.

Other drivers exist: PyMySQL (pure Python) and mysqlclient (a C extension used by Django). They follow the same Python database API (PEP 249), so connect(), cursor(), execute(), fetchone(), commit() and rollback() work almost identically. For a quick experiment without any server, Python's built-in sqlite3 module follows the same pattern, though it uses ? placeholders instead of %s.

Connections

mysql.connector.connect() takes the server address, credentials and database name. Read them from environment variables rather than writing them in the code:

Python
import os
import mysql.connector

connection = mysql.connector.connect(
    host=os.environ["MYSQL_HOST"],
    port=int(os.getenv("MYSQL_PORT", "3306")),
    user=os.environ["MYSQL_USER"],
    password=os.environ["MYSQL_PASSWORD"],
    database=os.environ["MYSQL_DATABASE"],
)

Connection errors raise mysql.connector.Error subclasses with useful messages, such as access denied or unknown database. Create a dedicated database user for your application with only the permissions it needs, never the root account.

Open a connection, do a unit of work, and close it. Long-running applications reuse connections through a pool (mysql.connector.pooling.MySQLConnectionPool) or an ORM such as SQLAlchemy, rather than connecting for every query. The database connections lesson discusses this in more depth.

Parameters

Never build SQL by inserting values into the string with f-strings, + or %. Pass values separately as parameters, and let the driver send them safely:

Python
member_id = 7
cursor.execute("SELECT name, email FROM members WHERE member_id = %s", (member_id,))
row = cursor.fetchone()        # a tuple, or None if no row matched

Connector/Python uses %s as the placeholder for every type, numbers included, and the parameters must be a tuple (note the comma in (member_id,)) or a list. Named placeholders also work: %(member_id)s with a dictionary.

Parameters stop SQL injection. If a search box value such as x' OR '1'='1 were pasted into the SQL text, it would change the query's meaning; as a parameter, it's only ever treated as a value. Placeholders can stand only for values, not for table or column names. If those must vary, choose them from a fixed allow-list in your code.

Read results with fetchone(), fetchall() or by looping over the cursor. connection.cursor(dictionary=True) returns rows as dictionaries keyed by column name.

Transactions

Changes made with INSERT, UPDATE and DELETE happen inside a transaction. Connector/Python turns autocommit off by default, so nothing is saved until you call connection.commit(); connection.rollback() abandons the pending changes:

Python
try:
    cursor.execute("UPDATE books SET on_loan = 1 WHERE book_id = %s", (42,))
    cursor.execute("INSERT INTO loans (book_id, member_id) VALUES (%s, %s)", (42, 7))
    connection.commit()          # both changes saved together
except mysql.connector.Error:
    connection.rollback()        # neither change saved
    raise

Grouping related changes in one transaction keeps the data consistent: a book is never marked as on loan without a matching loan record. Setting connection.autocommit = True makes every statement commit immediately, which is convenient for scripts but loses this protection.

Always close cursors and connections, even after errors, with try/finally as in the example below. Unclosed connections hold server resources and can exhaust the server's connection limit.

Independent practice and a worked check

Task: use a local sample MySQL database and parameterized loan queries, documenting set-up and clean-up. Create the schema in the MySQL client first:

SQL
CREATE DATABASE library_demo;
USE library_demo;
CREATE TABLE loans (loan_id INT AUTO_INCREMENT PRIMARY KEY, member_id INT, title VARCHAR(100));

Then, with MYSQL_DATABASE=library_demo and the other variables set:

Python
import os
import mysql.connector

conn = mysql.connector.connect(host=os.environ["MYSQL_HOST"], user=os.environ["MYSQL_USER"],
                               password=os.environ["MYSQL_PASSWORD"], database=os.environ["MYSQL_DATABASE"])
try:
    cur = conn.cursor()
    cur.execute("INSERT INTO loans (member_id, title) VALUES (%s, %s)", (7, "River Atlas"))
    conn.commit()
    cur.execute("SELECT title FROM loans WHERE member_id = %s", (7,))
    print(cur.fetchall())          # [('River Atlas',)]
    cur.close()
finally:
    conn.close()

Clean up afterwards with DROP DATABASE library_demo;. For the worked check, a member identifier should enter a SQL query as a bound parameter.

Reference and next practice

The MySQL Connector/Python developer guide documents connection options, cursors and errors, and PEP 249 defines the common database API. Continue with SQL CRUD in Python and database connections. Practise with the Python exercises and Python quiz.

Python MySQL Tutorial Summary

  • Driver setup: MySQL Connector/Python is a separately installed database driver.
  • Connections: A connection uses server details and credentials supplied by configuration.
  • Parameters: Connector/Python uses parameter placeholders to bind values separately from SQL.
  • Transactions: commit saves pending changes and rollback abandons them when the transaction fails.

Python MySQL Tutorial Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Python MySQL Tutorial in a small program

This needs a MySQL server with a members table and the four environment variables set. It prints one row as a tuple, such as ('Ana Ruiz',), or None if member 7 doesn't exist.

Python
import os
import mysql.connector

connection = mysql.connector.connect(
    host=os.environ["MYSQL_HOST"],
    user=os.environ["MYSQL_USER"],
    password=os.environ["MYSQL_PASSWORD"],
    database=os.environ["MYSQL_DATABASE"],
)
try:
    cursor = connection.cursor()
    try:
        cursor.execute("SELECT name FROM members WHERE member_id = %s", (7,))
        print(cursor.fetchone())
    finally:
        cursor.close()
finally:
    connection.close()
Line-by-line explanation
  1. Lines 4 to 9Connection details come from environment variables, so no password appears in the code.
  2. Lines 11 to 13The member id is passed as a parameter tuple, never inserted into the SQL text.
  3. Line 14fetchone() returns the first row or None.
  4. Lines 15 to 18The nested finally blocks close the cursor and the connection even if the query fails.
Example 2

The same pattern with sqlite3, runnable anywhere

Python's built-in sqlite3 follows the same database API, so you can practise the workflow without a server. Note the ? placeholder.

  • In MySQL Connector/Python the same code uses %s instead of ?.
Python
import sqlite3

connection = sqlite3.connect(":memory:")
cursor = connection.cursor()
cursor.execute("CREATE TABLE members (member_id INTEGER PRIMARY KEY, name TEXT)")
cursor.executemany("INSERT INTO members (member_id, name) VALUES (?, ?)",
                   [(7, "Ana Ruiz"), (8, "Ben Okafor")])
connection.commit()

search = "x' OR '1'='1"
cursor.execute("SELECT name FROM members WHERE name = ?", (search,))
print("Injection attempt matched:", cursor.fetchall())

cursor.execute("SELECT name FROM members WHERE member_id = ?", (7,))
print(cursor.fetchone())

try:
    cursor.execute("UPDATE members SET name = ? WHERE member_id = ?", ("Ana R.", 7))
    raise RuntimeError("simulated failure before commit")
except RuntimeError:
    connection.rollback()
cursor.execute("SELECT name FROM members WHERE member_id = 7")
print(cursor.fetchone())
connection.close()
Output
Injection attempt matched: []
('Ana Ruiz',)
('Ana Ruiz',)
Line-by-line explanation
  1. Lines 10 to 12As a parameter, the malicious text is just a name that matches nothing.
  2. Lines 17 to 21The update is rolled back, so the original name is still stored.

Common Python MySQL Tutorial Mistakes

Building SQL with an f-string

Don't
name = input("Member name: ")
cursor.execute(f"SELECT * FROM members WHERE name = '{name}'")   # SQL injection
Do
name = input("Member name: ")
cursor.execute("SELECT * FROM members WHERE name = %s", (name,))

Forgetting to commit

Don't
cursor.execute("INSERT INTO loans (book_id, member_id) VALUES (%s, %s)", (42, 7))
connection.close()      # the insert is discarded
Do
cursor.execute("INSERT INTO loans (book_id, member_id) VALUES (%s, %s)", (42, 7))
connection.commit()
connection.close()

Python MySQL Tutorial in Real Projects

MySQL and Python in real applications

Web frameworks connect to MySQL through drivers like this one, usually behind an ORM: Django uses mysqlclient, and SQLAlchemy supports Connector/Python, PyMySQL and mysqlclient. Data scripts use the driver directly, or pandas.read_sql() with a SQLAlchemy engine, to pull query results into a DataFrame.

In production, connection details come from environment variables or a secrets manager, applications connect with a least-privilege user over TLS, and every value from a user goes through parameters. Transactions wrap any multi-step change, such as creating an order and reducing stock, so a failure halfway leaves the database consistent.

Python MySQL Tutorial FAQ

How do I connect Python to a MySQL database?

Install a driver with python -m pip install mysql-connector-python, then call mysql.connector.connect(host=..., user=..., password=..., database=...). Create a cursor with connection.cursor() and run SQL with cursor.execute().

Why are my inserts not saved in MySQL?

Connector/Python doesn't autocommit by default. Call connection.commit() after your changes, or set connection.autocommit = True if every statement should be saved immediately.

Which MySQL driver should I use with Python?

mysql-connector-python is Oracle's official driver. PyMySQL is a pure-Python alternative, and mysqlclient is a fast C-based driver that Django recommends. All follow the same Python database API.

Why does MySQL use %s instead of ? for placeholders?

Each driver chooses a parameter style defined by PEP 249. MySQL drivers use the "format" style %s (and %(name)s), while sqlite3 uses ?. In both cases the values are sent separately; %s here isn't Python string formatting.

How do I get the ID of the row I just inserted?

After cursor.execute("INSERT ..."), read cursor.lastrowid. It holds the AUTO_INCREMENT value generated for that insert.

Python MySQL Tutorial Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

How should a member identifier enter a SQL query?

Multiple choicemedium

Which review concern is valid for python mysql tutorial?

Python MySQL Tutorial Quiz

2 questions to check what you learned.

  1. How should a member identifier enter a SQL query?

    • By concatenating user text
    • As a bound parameter
    • Python ignores the distinction
  2. Which statement correctly describes parameters?

    • Connector/Python uses parameter placeholders to bind values separately from SQL.
    • Only the example output matters, not the rule
    • Every operation has the same behavior