BizTechLab

IDEASINNOVATIONIMPACT

Database Concepts & Theory

The SQL Sub-Languages: DDL, DML, DQL, DCL & TCL

Every SQL statement belongs to one of five sub-languages — knowing which clarifies what it actually does to the system.

2 August 20266 min read

Overview

SQL isn't one flat list of commands — every statement belongs to one of five sub-languages, grouped by what kind of thing it actually changes. DDL changes the schema itself. DML changes data. DQL reads data. DCL changes permissions. TCL controls where a transaction begins and ends. Knowing which sub-language a statement belongs to tells you its real blast radius before you run it — a `DROP TABLE` and a `SELECT` are both 'just SQL,' but they are not remotely the same kind of operation.

Why It Exists

This grouping isn't academic — it maps directly onto who should be allowed to run what, and how the database engine itself treats each kind of statement. Most engines auto-commit DDL immediately and separately from your transaction, many production teams route DDL through a reviewed migration tool instead of letting application code run it directly, and DCL is typically restricted to database administrators entirely. Understanding the five sub-languages is what lets you reason about a statement's risk before running it, instead of treating every line of SQL as equally safe.

Real World Example

In a typical order-processing system: a migration tool runs the DDL that created the `orders` and `customers` tables in the first place. The application runs DML (`INSERT`, `UPDATE`) to record new orders, and DQL (`SELECT`) constantly, to read them back. A database administrator runs DCL (`GRANT`) to give a new reporting service read-only access to the `orders` table, without giving it the ability to modify anything. And the order-placement code wraps its multi-step DML in TCL (`BEGIN` ... `COMMIT`), so that debiting inventory and creating the order row either both succeed or both roll back together.

Example Data

orders — the table DDL created, DML writes to, and DQL reads from

idcustomer_idproductstatus
1011Laptopshipped
1021Mouseprocessing

How It Works

  • DDL (Data Definition Language)CREATE, ALTER, DROP, TRUNCATE — defines and changes the schema itself: tables, columns, indexes, constraints.
  • DML (Data Manipulation Language)INSERT, UPDATE, DELETE — changes the data inside existing tables.
  • DQL (Data Query Language)SELECT — reads data without changing it (some references fold this into DML, but it's worth treating separately since it's the statement you'll write most often).
  • DCL (Data Control Language)GRANT, REVOKE — controls who can do what, at the permissions layer.
  • TCL (Transaction Control Language)BEGIN, COMMIT, ROLLBACK, SAVEPOINT — controls where a transaction's boundaries are, wrapping a group of DML statements so they succeed or fail together.

The Five Sub-Languages, One at a Time

DDL — Data Definition Language

CREATEALTERDROPTRUNCATE

DDL defines and changes the schema itself — the tables, columns, indexes, and constraints that give a database its structure. It's purely structural: a DDL statement never touches the rows inside a table, only the container those rows live in. That distinction matters operationally, too — most engines auto-commit every DDL statement immediately and separately from whatever transaction it ran inside, so a `CREATE TABLE` can't be rolled back the way an `INSERT` can. Because DDL changes are often slow, can lock the table being altered, and are occasionally irreversible (`DROP TABLE` doesn't ask twice), production teams route it through reviewed, versioned migration tooling instead of letting application code run it directly.

CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT NOT NULL,
  product TEXT NOT NULL
);

ALTER TABLE orders ADD COLUMN status TEXT DEFAULT 'pending';

DROP TABLE old_orders;
Try It in the Playground

DML — Data Manipulation Language

INSERTUPDATEDELETE

DML changes the data living inside a table whose structure already exists — adding new rows, changing existing ones, or removing them. Where DDL operates on schema, DML operates on rows, and its effects are governed by whatever TCL transaction boundary it happens to run inside: a `DELETE` inside an open transaction can still be rolled back, right up until that transaction commits. DML is also where most application-level bugs around consistency actually happen — an `UPDATE` that only touches half of what it should, or a `DELETE` that runs without a `WHERE` clause, is a DML mistake, not a schema mistake.

INSERT INTO orders (id, customer_id, product) VALUES (101, 1, 'Laptop');

UPDATE orders SET status = 'shipped' WHERE id = 101;

DELETE FROM orders WHERE id = 101;
Try It in the Playground

DQL — Data Query Language

SELECT

DQL reads data without changing anything — and in almost every real application, `SELECT` is the single statement you'll write more than every other kind combined. Some references fold DQL into DML entirely, treating it as just another data operation, but it's worth keeping separate: reading has fundamentally different performance and locking characteristics than writing. A DQL statement can still be expensive — an unindexed `SELECT` on a large table can saturate disk I/O as badly as a heavy write — so it deserves the same profiling attention as any DML statement, not a free pass just because it doesn't modify data.

SELECT id, product, status
FROM orders
WHERE customer_id = 1
ORDER BY id DESC;
Try It in the Playground

DCL — Data Control Language

GRANTREVOKE

DCL controls who is allowed to do what, at the permissions layer — entirely independent of the data or schema themselves. It's how a database enforces the principle of least privilege in practice: a reporting service can be granted read-only access to exactly the tables it needs, with no path to ever modify them, regardless of what its application code tries to do. DCL is typically restricted to database administrators rather than exposed to general application code, since a mistake here doesn't corrupt one row — it can open or close access for an entire service.

GRANT SELECT ON orders TO reporting_service;

REVOKE INSERT, UPDATE, DELETE ON orders FROM reporting_service;

TCL — Transaction Control Language

BEGINCOMMITROLLBACKSAVEPOINT

TCL controls where a transaction's boundaries are, wrapping a group of DML statements so they succeed or fail together as one atomic unit. Without an explicit TCL boundary, a multi-statement operation is really just a sequence of independent statements — if the third of five `UPDATE`s fails, the first two have already committed, and the data is left in a state the application never intended to be possible. `SAVEPOINT` extends this further, letting you roll back to a specific point inside a larger transaction instead of discarding the whole thing. TCL is the direct mechanism behind the Atomicity in ACID, covered later in this journey.

BEGIN;

UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;

COMMIT;
Try It in the Playground

Diagram

Every SQL statement falls into exactly one of these five categories

DDL

CREATE, ALTER, DROP — defines the schema

DML

INSERT, UPDATE, DELETE — changes data

DQL

SELECT — reads data

DCL

GRANT, REVOKE — controls permissions

TCL

BEGIN, COMMIT, ROLLBACK — controls transactions

Common Mistakes

Running raw DDL from application code directly against production

Why: DDL changes are often slow, locking, and sometimes irreversible — running them outside of a reviewed migration process skips the safety net that catches mistakes before they hit real data.

Fix: Route every DDL change through versioned migration tooling with review, never as an ad-hoc statement from the app.

Treating SELECT statements as free of any real impact on the system

Why: A DQL statement can still lock rows or tables depending on the isolation level, and an unindexed SELECT on a large table can saturate disk I/O just as badly as a heavy write.

Fix: Load-test and profile read paths with the same seriousness as write paths — DQL isn't exempt from performance problems.

Running a multi-statement operation as separate, unwrapped DML statements instead of inside a transaction

Why: Without TCL wrapping related writes, each statement commits independently — a failure partway through can leave the data in a state that was never supposed to be possible.

Fix: Wrap related writes that must succeed or fail together in `BEGIN` ... `COMMIT` — this is the foundation the ACID chapter builds directly on.

Interview Questions

beginner

What's the difference between DDL and DML?

DDL (`CREATE`, `ALTER`, `DROP`) changes the schema — the structure of tables and columns themselves. DML (`INSERT`, `UPDATE`, `DELETE`) changes the data stored inside a schema that already exists, without altering its structure.

intermediate

Why do many teams restrict who can run DDL and DCL in a production database?

DDL can change or destroy the structure data depends on, and DCL controls who has access to what — both have a much larger blast radius than a normal data read or write. Restricting them to reviewed processes and database administrators limits how much damage a single mistake or compromised credential can do.

senior

Why does TCL matter even for a single-statement operation?

A single DML statement is technically atomic on its own in most engines, but TCL becomes essential the moment an operation spans more than one statement that must succeed or fail as a unit — like debiting one account and crediting another. Without an explicit transaction boundary, a failure partway through leaves the system in a state that violates an invariant the application assumed could never happen.

Production Best Practices

Do

Route DDL through versioned, reviewed migration tooling instead of running it ad hoc.

Wrap multi-statement DML that must succeed or fail together inside explicit TCL boundaries.

Restrict DCL to database administrators or a tightly controlled process, not general application code.

Don't

Don't run schema changes directly from application code in production.

Don't assume a SELECT is free of performance impact just because it doesn't write data.

Don't leave related writes as separate, unwrapped statements when a partial failure would leave invalid data.

Comparison

Example StatementsChanges
DDLCREATE, ALTER, DROPSchema structure
DMLINSERT, UPDATE, DELETEData
DQLSELECTNothing — reads only
DCLGRANT, REVOKEPermissions
TCLBEGIN, COMMIT, ROLLBACKTransaction boundaries