Dynamic SQL — Building and Executing SQL on the Fly
Dynamic SQL
A report accepts a city value and an approved sort choice from the user. Both affect the request, but they cannot be handled with the same substitution rule.
Dynamic SQL constructs or prepares a statement at runtime. The SQL structure and its data values require different handling.
Explain and apply
Build a MySQL prepared statement and keep user-supplied values separate from its SQL structure.
Work it out
Prepare a MySQL query with one city value parameter and execute it for Pune.
Avoid mistakes
Concatenating an untrusted value into SQL is unsafe.
Dynamic SQL: the request and its result
School need
A report accepts a city value and an approved sort choice from the user. Both affect the request, but they cannot be handled with the same substitution rule.
Database operation
Dynamic SQL constructs or prepares a statement at runtime. The SQL structure and its data values require different handling.
Check the outcome
The statement structure stays fixed while the city value is supplied separately. A sort-column name would instead need selection from trusted SQL choices.
Understand what the database does
Bind data values through supported parameter markers. Identifiers and sort directions are part of SQL structure and usually cannot be replaced by a value parameter; choose them from an explicit allowlist instead. MySQL PREPARE and EXECUTE implement server-side prepared statements, with restrictions inside stored programs.
Related concepts: Prevent SQL Injection and SQL with Python.
What is Dynamic SQL?
Dynamic SQL is a SQL statement created and executed at runtime rather than being hardcoded into the procedure. You use it when the structure of your query is not fixed in advance — for example, when filtering by variable columns, table names, or sorting criteria.
MySQL Syntax Overview
PREPARE stmt_name FROM @sqlEXECUTE stmt_nameDEALLOCATE PREPARE stmt_name
Sample Table – students
MySQL 8.4 · prepared statement
CREATE TABLE students (
roll_no INT,
name VARCHAR(50),
marks INT,
city VARCHAR(30)
);
INSERT INTO students VALUES
(1, 'Aarav Sharma', 85, 'Delhi'),
(2, 'Diya Iyer', 92, 'Chennai'),
(3, 'Sneha Patil', 78, 'Pune'),
(4, 'Mehul Agarwal', 90, 'Delhi');
1. Basic Dynamic SQL – Select by City
Input definitions and data for this example. The shown statements establish the base fixture. Where this section changes an existing object, its required object definition is included below; use the stated before/after table to follow a sequence of changes.
CREATE TABLE students (
roll_no INT,
name VARCHAR(50),
marks INT,
city VARCHAR(30)
);
INSERT INTO students VALUES
(1, 'Aarav Sharma', 85, 'Delhi'),
(2, 'Diya Iyer', 92, 'Chennai'),
(3, 'Sneha Patil', 78, 'Pune'),
(4, 'Mehul Agarwal', 90, 'Delhi');
MySQL 8.4 · prepared statement
SET @city_name = 'Delhi';
SET @sql = CONCAT('SELECT name, marks FROM students WHERE city = "', @city_name, '"');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
name | marks
----------------+-------
Aarav Sharma | 85
Mehul Agarwal | 90
2. Sorting Dynamically
Input definitions and data for this example. The shown statements establish the base fixture. Where this section changes an existing object, its required object definition is included below; use the stated before/after table to follow a sequence of changes.
CREATE TABLE students (
roll_no INT,
name VARCHAR(50),
marks INT,
city VARCHAR(30)
);
INSERT INTO students VALUES
(1, 'Aarav Sharma', 85, 'Delhi'),
(2, 'Diya Iyer', 92, 'Chennai'),
(3, 'Sneha Patil', 78, 'Pune'),
(4, 'Mehul Agarwal', 90, 'Delhi');
MySQL 8.4 · prepared statement
SET @sort_column = 'marks';
SET @sql = CONCAT('SELECT name, marks FROM students ORDER BY ', @sort_column, ' DESC');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
name | marks
----------------+-------
Diya Iyer | 92
Mehul Agarwal | 90
Aarav Sharma | 85
Sneha Patil | 78
3. Inside a Stored Procedure
Input definitions and data for this example. The shown statements establish the base fixture. Where this section changes an existing object, its required object definition is included below; use the stated before/after table to follow a sequence of changes.
CREATE TABLE students (
roll_no INT,
name VARCHAR(50),
marks INT,
city VARCHAR(30)
);
INSERT INTO students VALUES
(1, 'Aarav Sharma', 85, 'Delhi'),
(2, 'Diya Iyer', 92, 'Chennai'),
(3, 'Sneha Patil', 78, 'Pune'),
(4, 'Mehul Agarwal', 90, 'Delhi');
MySQL 8.4 · prepared statement
DELIMITER $$
CREATE PROCEDURE get_students_by_city(IN p_city VARCHAR(30))
BEGIN
SET @sql = CONCAT('SELECT name, marks FROM students WHERE city = "', p_city, '"');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Call it:
CALL get_students_by_city('Pune');
When to Use Dynamic SQL
- When table, column, or filter values are determined at runtime
- When building advanced search or reporting systems
- When writing reusable admin tools or dashboards
Best Practices
Query discussed in this section:
CALL get_students_by_city('Pune');
- Always sanitize input to avoid SQL injection risks
- Use prepared statements instead of directly executing dynamic strings
- Use stored procedures to encapsulate dynamic SQL safely
Real-World School Use Cases
- Generate student reports by city or class dynamically
- Create dashboards where the admin selects filter criteria
- Allow dynamic sorting and pagination of student data
Important differences and mistakes
Concatenating an untrusted value into SQL is unsafe. Escaping manually is not a substitute for parameter binding. Dynamic SQL is unnecessary when one static parameterized query already expresses the task.
Try it with the school data
Prepare a MySQL query with one city value parameter and execute it for Pune.
This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.
| roll_no | name | class | marks | city |
|---|---|---|---|---|
| 1 | Aarav | 10A | 85 | Pune |
| 2 | Diya | 9B | 70 | Chennai |
| 3 | Sneha | 10A | 85 | Pune |
SQL · create only these practice tables in a disposable database:
CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');
Worked answer and reasoning
This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.
| roll_no | name | class | marks | city |
|---|---|---|---|---|
| 1 | Aarav | 10A | 85 | Pune |
| 2 | Diya | 9B | 70 | Chennai |
| 3 | Sneha | 10A | 85 | Pune |
SQL · create only these practice tables in a disposable database:
CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');
MySQL 8.4 · prepared statement
PREPARE practice_report FROM 'SELECT name FROM practice_students WHERE city = ? ORDER BY roll_no';
SET @practice_city = 'Pune';
EXECUTE practice_report USING @practice_city;
DEALLOCATE PREPARE practice_report;| name |
|---|
| Aarav |
| Sneha |
The statement structure stays fixed while the city value is supplied separately. A sort-column name would instead need selection from trusted SQL choices.
Run this engine-specific script on a fresh MySQL 8.4 practice schema with the required routine privileges. It is not executable in SQLite.
Check your understanding
Check your understanding
Use the idea independently
Dynamic SQL constructs or prepares a statement at runtime. The SQL structure and its data values require different handling.
The statement structure stays fixed while the city value is supplied separately. A sort-column name would instead need selection from trusted SQL choices.
Continue your understanding with Prevent SQL Injection and SQL with Python.
Source for the engine-specific behavior: MySQL 8.4 reference documentation.
Reaching the recap and completing the quizzes are separate milestones.