DevLearningTools

LEARN · SQL

SQL Parameters

A parameter is a placeholder that keeps a value separate from a query's SQL text, sent to the database as pure data, never as code. Learn the real placeholder syntax, by database.

The previous lesson's vulnerable search, query = "SELECT * FROM employees WHERE last_name = '" + userInput + "'", is exploitable because userInput becomes part of the SQL text itself. A parameter fixes exactly that: the query's structure and the value stay two separate things, sent to the database separately.

Learning Objectives

  • Define a parameter precisely: a placeholder in the query, with its value sent separately, never concatenated.
  • Distinguish positional placeholders from named placeholders.
  • Rewrite a concatenated, injectable query as a parameterized one.

SQL Text (Fixed)

WHERE last_name = ?

Placeholder

? or :name or $1

Value Sent Separately

Never part of the SQL text

Executed Safely

Input can't change the query's structure

What a Parameter Actually Is

A parameter is a placeholder written directly into a query's SQL text, with the actual value supplied separately, through the database driver, not pasted into the string. The database always treats a parameter's value as pure data, there's no way for it to be interpreted as SQL syntax, no matter what characters it contains. ' OR '1'='1 passed as a parameter value searches for an employee whose literal last name is the string ' OR '1'='1, it can't escape out into the query's structure the way it could through concatenation.

Rewriting the Vulnerable Search

The SQL text is now fixed, it never changes no matter what userInput contains. Only the value passed alongside it changes, and the database never re-interprets that value as part of the query's structure.

Vulnerable (previous lesson)
query = "SELECT * FROM employees WHERE last_name = '" + userInput + "'"
Parameterized (pseudocode, positional placeholder)
query = "SELECT * FROM employees WHERE last_name = ?"
run(query, [userInput])

Placeholder Syntax by Database

DatabaseTypical Placeholder Style
MySQL / MariaDB? positional (most drivers), some also support :name named placeholders
PostgreSQL$1, $2, ... positional, numbered
SQL Server@name named (ADO.NET/ODBC), or ? positional depending on the driver
Oracle:name named bind variables
SQLite? positional, or :name, @name, $name named, all four are accepted
NOTE

This table describes typical driver-level convention, not a single universal SQL standard, the exact placeholder style actually available depends on which client library or driver is making the call, not just which database is on the other end.

Try It Yourself

Rewrite this vulnerable query using a positional parameter: query = "SELECT * FROM employees WHERE department_id = " + userInput.

Answer
query = "SELECT * FROM employees WHERE department_id = ?"
run(query, [userInput])

Frequently Asked Questions

Does parameterizing a query slow it down?

No, if anything it tends to help: many databases can cache the parsed query plan for a parameterized statement and reuse it across executions with different values, something a freshly concatenated string generally can't benefit from in the same way.

Can a table or column name be parameterized the same way a value can?

No, a parameter can only stand in for a value, never for an identifier like a table or column name, that's a real remaining gap. A query that needs a dynamic table or column name has to validate it against a fixed allow-list of known-safe names instead, parameters don't cover that case.

Common Beginner Mistakes

Parameterizing the value but still concatenating the table or column name

ORDER BY " + columnName is still exploitable even if every value elsewhere in the same query is properly parameterized, parameters don't apply to identifiers. columnName needs to be checked against a fixed allow-list of real column names before being concatenated, never trusted directly.

Assuming every database uses the same placeholder character

? works on some databases and drivers, $1 on others, :name or @name elsewhere, and SQLite happens to accept all four styles. Always confirm the actual placeholder syntax for the specific database and driver in use, not an assumption carried over from a different one.

Best Practices

  • Parameterize every value that comes from outside the application's own code, with no exceptions for values that "look safe."
  • Validate dynamic table/column names against a fixed allow-list, since parameters can't protect identifiers.
  • Let the database driver or ORM handle placeholder syntax, rather than hand-building parameter strings.

Interview Questions

Explain exactly why a parameterized query is safe from the injection shown in the previous lesson.

With query = "SELECT * FROM employees WHERE last_name = ?" and the value passed separately as a parameter, the SQL text itself never changes, regardless of what the value contains. A value of ' OR '1'='1 is treated as a literal string to search for, not as SQL syntax, so it can't reshape the WHERE clause the way it could when concatenated directly into the query's text.

Why can't SQL injection through a column name be fixed by parameterizing it?

Parameters only ever stand in for values, never for identifiers like table or column names, no database's parameter mechanism supports binding an identifier that way. A dynamic ORDER BY columnName has to be validated against a fixed allow-list of real, known column names before being inserted into the query, parameters simply don't apply to that case.

Summary

A parameter keeps a value separate from a query's SQL text, sent to the database as pure data, so it can never be reinterpreted as part of the query's structure, closing the exact vulnerability the previous lesson demonstrated. The placeholder character itself (?, $1, :name, @name) differs by database and driver, but the underlying mechanism is the same everywhere. Parameters only cover values, never table or column names.

What's Next?

Prepared Statements is next: the prepare-once, execute-many mechanism that actually uses parameters under the hood, and how it's a genuinely different thing from just passing a parameter once.

</>

Ready to Practice SQL?

Write and run real SQL queries online, right in your browser, no install or signup required. Pick a playground based on which database you want to practice on.

SQLite / PostgreSQL

Fast, no server, no ads.

Open DB Fiddle ↗

MySQL / SQL Server

Switch databases with one dropdown.

Open SQL Fiddle ↗

Both open a third-party SQL playground in a new tab. External site terms and privacy policy apply.

Or try 50 SQL practice questions →