---
title: "03 — DDL: Shaping the Tables Themselves"
uid: ddl-shaping-tables
tags: ["sql", "roadmap:sql", "alter", "truncate", "create", "ddl", "drop", "schema"]
excerpt: "DDL is the SQL subset that changes the shape of the database itself — creating, altering, dropping structures — not the rows inside them."
date: 2026-08-13T03:27:38+0000
source: https://www.aveshina.my.id/en/blog/ddl-shaping-tables
---

"Just the setup script" was how I filed DDL and then forgot it, which made schema changes feel riskier than they are. Writing it down separated one idea from the day-to-day verbs: **DDL is the SQL subset that changes the shape of the database itself — creating, altering, emptying, and dropping tables and other structures — not the rows inside them.** [1][2]

The framing that clicked is the split between *defining* and *using*. The four CRUD verbs operate on data within a structure that already exists. DDL is what brings that structure into being, reshapes it, or removes it. CREATE TABLE defines the columns and types; after that, INSERT and SELECT have something to work against. Mess with DDL and you're editing the stage, not the actors.

```figure
<svg viewBox="0 0 680 260" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="The four DDL commands and their effect on a table. CREATE TABLE assembles an empty grid. ALTER TABLE adds a column to an existing grid. TRUNCATE TABLE empties the rows but keeps the grid. DROP TABLE removes the grid entirely.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- CREATE -->
    <text x="95" y="34" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">CREATE TABLE</text>
    <text x="95" y="48" font-size="9" fill="#64748b" text-anchor="middle">brings the grid into being</text>
    <rect x="40" y="60" width="110" height="80" rx="4" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <line x1="40" y1="80" x2="150" y2="80" stroke="#6366f1"/>
    <line x1="95" y1="60" x2="95" y2="140" stroke="#6366f1"/>
    <line x1="72" y1="60" x2="72" y2="140" stroke="#6366f1" opacity="0.4"/>

    <!-- ALTER -->
    <text x="265" y="34" font-size="12" font-weight="700" fill="#422006" text-anchor="middle">ALTER TABLE</text>
    <text x="265" y="48" font-size="9" fill="#64748b" text-anchor="middle">adds / changes a column</text>
    <rect x="200" y="60" width="130" height="80" rx="4" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <line x1="200" y1="80" x2="330" y2="80" stroke="#ca8a04"/>
    <line x1="245" y1="60" x2="245" y2="140" stroke="#ca8a04"/>
    <line x1="290" y1="60" x2="290" y2="140" stroke="#ca8a04"/>
    <rect x="290" y="60" width="40" height="80" fill="#fde047" opacity="0.5"/>
    <text x="310" y="104" font-size="9" fill="#422006" text-anchor="middle">new</text>

    <!-- TRUNCATE -->
    <text x="435" y="34" font-size="12" font-weight="700" fill="#134e4a" text-anchor="middle">TRUNCATE TABLE</text>
    <text x="435" y="48" font-size="9" fill="#64748b" text-anchor="middle">empties rows, keeps shape</text>
    <rect x="380" y="60" width="110" height="80" rx="4" fill="#ccfbf1" stroke="#0d9488" stroke-width="1.5"/>
    <line x1="380" y1="80" x2="490" y2="80" stroke="#0d9488"/>
    <line x1="435" y1="60" x2="435" y2="140" stroke="#0d9488"/>
    <line x1="412" y1="60" x2="412" y2="140" stroke="#0d9488" opacity="0.4"/>

    <!-- DROP -->
    <text x="605" y="34" font-size="12" font-weight="700" fill="#7f1d1d" text-anchor="middle">DROP TABLE</text>
    <text x="605" y="48" font-size="9" fill="#64748b" text-anchor="middle">removes the grid entirely</text>
    <rect x="540" y="60" width="110" height="80" rx="4" fill="none" stroke="#dc2626" stroke-width="1.5" stroke-dasharray="4,4"/>
    <text x="595" y="106" font-size="16" fill="#dc2626" text-anchor="middle">— gone —</text>

    <text x="340" y="180" font-size="10" fill="#64748b" text-anchor="middle" font-style="italic">CREATE/ALTER change the schema; TRUNCATE clears data fast; DROP deletes the structure</text>
    <text x="340" y="200" font-size="10" fill="#64748b" text-anchor="middle" font-style="italic">DDL operates on the table object, not the individual rows</text>
  </g>
</svg>
```

## CREATE TABLE — define the shape

CREATE TABLE brings a table into existence with its name, columns, types, and constraints [1][2]:

```
CREATE TABLE customers (
  id          INTEGER PRIMARY KEY,
  name        VARCHAR(100) NOT NULL,
  email       VARCHAR(255) UNIQUE,
  created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

This is the contract every later INSERT and SELECT obeys. The column types (INTEGER, VARCHAR, TIMESTAMP) decide what fits; the constraints (PRIMARY KEY, NOT NULL, UNIQUE, DEFAULT) decide what's allowed. I think of CREATE TABLE as writing the rules the data has to live by.

## ALTER TABLE — change an existing shape

ALTER TABLE modifies a table that already exists — adding, dropping, or renaming columns, changing types, or attaching constraints [3][4]:

```
ALTER TABLE customers ADD COLUMN phone VARCHAR(20);
ALTER TABLE customers DROP COLUMN deprecated_field;
```

This is DDL's real superpower and its real risk: it reshapes a structure that may already hold millions of rows. Adding a nullable column is cheap; changing a column's type or adding a NOT NULL constraint can trigger a full table rewrite. In production I treat every ALTER as a migration to sequence carefully, not a one-off command.

## DROP TABLE — remove the structure

DROP TABLE deletes the table and all its data, permanently [5]:

```
DROP TABLE customers;
```

There's no WHERE, no undo. Once dropped, the structure and every row are gone unless a backup exists. I treat DROP the way I treat rm -rf — read the statement twice, and confirm the table name.

## TRUNCATE TABLE — empty it, fast

TRUNCATE TABLE removes all rows but keeps the table structure intact [6][7]:

```
TRUNCATE TABLE staging_imports;
```

The distinction from DELETE FROM staging_imports; is the part I had to nail down. DELETE is a DML operation that logs every row removal, so it's safe, transactional, and slow for big clears. TRUNCATE is DDL — it deallocates the data pages wholesale, skipping per-row logging, which is why it's dramatically faster for wiping a table [6]. The cost: in many engines TRUNCATE can't be rolled back inside a transaction the way DELETE can. Reach for it on staging and log tables, not on anything where you might want the rows back.

## How I use this

The habit I keep from DDL is a mental category check before I run a statement: am I changing the *shape* (DDL — CREATE, ALTER, DROP, TRUNCATE) or the *contents* (DML — INSERT, UPDATE, DELETE)? If it's DDL on a live table, I write it as a versioned migration, never ad-hoc, because schema changes are the one class of edit that's hardest to roll back. And for bulk clears I reach for TRUNCATE only on disposable tables — anything transactional gets DELETE, accepting the slowness as the price of safety.

## References

[1] dbt Labs, "Data Definition Language (DDL)," docs.getdbt.com, 2024. [Online]. Available: [https://docs.getdbt.com/terms/ddl](https://docs.getdbt.com/terms/ddl)

[2] DbVisualizer, "The Definitive Guide on Data Definition Language," dbvis.com, 2024. [Online]. Available: [https://www.dbvis.com/thetable/sql-ddl-the-definitive-guide-on-data-definition-language/](https://www.dbvis.com/thetable/sql-ddl-the-definitive-guide-on-data-definition-language/)

[3] TechOnTheNet, "ALTER TABLE Statement," techonthenet.com, 2024. [Online]. Available: [https://www.techonthenet.com/sql/tables/alter_table.php](https://www.techonthenet.com/sql/tables/alter_table.php)

[4] PostgreSQL Tutorial, "ALTER TABLE," postgresqltutorial.com, 2024. [Online]. Available: [https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-alter-table/](https://www.postgresqltutorial.com/postgresql-tutorial/postgresql-alter-table/)

[5] Coginiti, "Drop a Table," coginiti.co, 2024. [Online]. Available: [https://www.coginiti.co/tutorials/beginner/drop-a-table/](https://www.coginiti.co/tutorials/beginner/drop-a-table/)

[6] TutorialsPoint, "TRUNCATE TABLE," tutorialspoint.com, 2024. [Online]. Available: [https://www.tutorialspoint.com/sql/sql-truncate-table.htm](https://www.tutorialspoint.com/sql/sql-truncate-table.htm)

[7] Programiz, "SQL CREATE TABLE," programiz.com, 2024. [Online]. Available: [https://www.programiz.com/sql/create-table](https://www.programiz.com/sql/create-table)

```quiz
Q: Which SQL subset changes the structure of the database rather than the rows in it?
- DML (INSERT/UPDATE/DELETE)
- DDL (CREATE/ALTER/DROP/TRUNCATE)
correct: 1
explain: DDL defines and modifies the schema — tables, columns, types. DML operates on the data within an already-defined structure.

Q: What's the key difference between DELETE and TRUNCATE?
- There is no difference; they are aliases
- DELETE logs each row removal (slow, transactional); TRUNCATE deallocates pages wholesale (fast, less rollback-friendly)
correct: 1
explain: TRUNCATE skips per-row logging by dropping data pages, making it much faster for clearing a table, at the cost of transactional rollback semantics in many engines.

Q: What does DROP TABLE do?
- Removes all rows but keeps the table structure
- Removes the table structure and all its data permanently
correct: 1
explain: DROP TABLE deletes the table object entirely. TRUNCATE is the one that empties rows but keeps the structure.

Q: Adding a NOT NULL column to a large existing table is risky because…
- it can require a full table rewrite to backfill every row
- SQL doesn't allow altering tables that contain data
correct: 0
explain: ALTER TABLE operations that change types or add constraints may force the engine to rewrite the whole table, which is expensive on large tables.

Q: Which command would you use to add a phone column to an existing customers table?
- ALTER TABLE customers ADD COLUMN phone VARCHAR(20)
- CREATE TABLE customers (phone VARCHAR(20))
correct: 0
explain: ALTER TABLE modifies an existing table. CREATE TABLE would attempt to create a brand-new table and fail if one already exists.
```
