SQL
MySQL
Parameterized Queries
Database Security
SQL Injection Prevention

MySQL parameterized queries

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

MySQL parameterized queries solve two problems at once: they keep SQL and user data separate, and they make application code much safer to maintain. The core idea is simple: write the SQL statement with placeholders, then pass the values separately so the driver can bind them correctly.

Why Parameters Matter

The unsafe pattern is string concatenation:

python
username = input("Username: ")
query = f"SELECT id, email FROM users WHERE username = '{username}'"

If username contains SQL syntax, the query text itself changes. That is how SQL injection happens.

A parameterized query keeps the SQL structure fixed:

python
1import mysql.connector
2
3conn = mysql.connector.connect(
4    host="localhost",
5    user="app_user",
6    password="secret",
7    database="app_db",
8)
9
10cursor = conn.cursor()
11
12username = "alice"
13cursor.execute(
14    "SELECT id, email FROM users WHERE username = %s",
15    (username,),
16)
17
18for row in cursor.fetchall():
19    print(row)

The %s marker is a placeholder, not Python string interpolation. The database driver sends the SQL text and the bound values separately.

Parameters Work for More Than SELECT

Prepared statements are just as important for inserts and updates:

python
1import mysql.connector
2
3conn = mysql.connector.connect(
4    host="localhost",
5    user="app_user",
6    password="secret",
7    database="app_db",
8)
9
10cursor = conn.cursor()
11
12user_id = 42
13new_email = "[email protected]"
14
15cursor.execute(
16    "UPDATE users SET email = %s WHERE id = %s",
17    (new_email, user_id),
18)
19
20conn.commit()
21print("Rows updated:", cursor.rowcount)

This avoids quoting mistakes and makes type handling more reliable. The driver knows which values are strings, numbers, dates, or NULL, and it formats them correctly for MySQL.

What Parameterization Does and Does Not Protect

Parameters protect values. They do not parameterize SQL keywords, column names, or table names. This means you can safely bind a username or date, but not an ORDER BY column name using the same placeholder mechanism.

If you need a dynamic identifier, whitelist it in application code:

python
1allowed_sort_columns = {"username", "created_at"}
2sort_column = "created_at"
3
4if sort_column not in allowed_sort_columns:
5    raise ValueError("Invalid sort column")
6
7query = f"SELECT id, username FROM users ORDER BY {sort_column}"

That is an important distinction. Parameters are for data values, not arbitrary SQL fragments.

Repeated Execution and Performance

Parameterized queries are usually discussed for security first, but they also help performance and clarity. When the same statement runs many times with different values, the driver and database can reuse the statement structure more efficiently than repeated string-built SQL.

For batch inserts, parameterization also makes code easier to scale:

python
1rows = [
2    ("alice", "[email protected]"),
3    ("bob", "[email protected]"),
4    ("carol", "[email protected]"),
5]
6
7cursor.executemany(
8    "INSERT INTO users (username, email) VALUES (%s, %s)",
9    rows,
10)
11
12conn.commit()

This is cleaner than constructing a long SQL string manually and safer than trying to quote every value yourself.

Common Pitfalls

The biggest mistake is using placeholders but still building part of the value with string concatenation before execution. If the untrusted text changes the SQL structure, parameterization loses its benefit.

Another common issue is trying to parameterize table names, column names, or ASC versus DESC. Drivers do not treat those as data values, so they must be handled with explicit allowlists.

It is also easy to forget the tuple syntax for a single value in Python drivers. (username,) is a one-item tuple, while (username) is just a parenthesized expression.

Summary

  • Parameterized queries keep SQL structure separate from runtime values.
  • They are the standard defense against SQL injection in MySQL application code.
  • Use placeholders for values in SELECT, INSERT, UPDATE, and DELETE statements.
  • Do not try to parameterize table names or column names; validate those explicitly instead.
  • Prepared statements also improve readability and help repeated query execution.

Course illustration
Course illustration

All Rights Reserved.