Using SQL from Application Code
Prerequisites: Inserting data, Constraints and foreign keys
Most SQL runs inside applications: a web app looks up a user, saves an order, or builds a report. Every language has a library for this.
Parameterized queries — always
Never build SQL by gluing user input into a string. Pass values as parameters instead, and the database driver keeps data and code separate.
In Python, with the built-in sqlite3 module:
import sqlite3
conn = sqlite3.connect("shop.db")
email = input("Email: ")
# DANGEROUS — SQL injection:
# conn.execute(f"SELECT * FROM customers WHERE email = '{email}'")
# Safe — the ? is a placeholder:
row = conn.execute("SELECT id, name FROM customers WHERE email = ?", (email,)).fetchone()
print(row)
In PHP, with PDO:
$pdo = new PDO('sqlite:shop.db');
$stmt = $pdo->prepare('SELECT id, name FROM customers WHERE email = :email');
$stmt->execute(['email' => $email]);
$customer = $stmt->fetch(PDO::FETCH_ASSOC);
What SQL injection looks like
If code builds "... WHERE email = '" + email + "'" and a user types:
' OR '1'='1
the query becomes WHERE email = '' OR '1'='1' — which is true for every
row, exposing every customer. Other inputs can delete tables. With
parameters, that input is just an odd email address that matches nothing.
SQL injection is one of the most common and damaging security bugs — and
parameters prevent it completely.
ORMs and query builders
Many frameworks provide an ORM (object-relational mapper) that maps tables to classes and writes common SQL for you: Laravel's Eloquent, Django's ORM, SQLAlchemy, Entity Framework, Hibernate, ActiveRecord, Prisma. They use parameters automatically and save a lot of repetitive code.
Knowing SQL still matters: ORMs generate SQL you'll need to read when debugging slow pages, and complex reports are often clearer in plain SQL.
The N+1 query problem
A classic ORM mistake: loading 100 orders, then running one query per
order to fetch its customer — 101 queries instead of 1 or 2. Use a join,
or the ORM's "eager loading" feature (with('customer'),
select_related, includes), to fetch related data in bulk.
Connections
Opening a database connection is slow. Applications reuse connections from a connection pool, and close them when finished. Keep transactions short, and don't hold a connection open while waiting on a user or an external API.