Every table used throughout this course started with a CREATE TABLE statement. This lesson covers it properly, column definitions, and the constraints beyond primary and foreign keys that stop bad data from ever being stored in the first place.
Learning Objectives
- Write a CREATE TABLE statement with columns, types, and constraints.
- Apply NOT NULL, UNIQUE, CHECK, and DEFAULT to enforce rules at the database level.
- Modify an existing table's structure with ALTER TABLE.
Define Columns
Name, type, per column
Add Constraints
NOT NULL, UNIQUE, CHECK, DEFAULT
Table Created
Rules enforced from day one
A Full CREATE TABLE Example
Every piece here, column types, NOT NULL, UNIQUE, CHECK, DEFAULT, and the foreign key, is standard SQL and works with this same syntax across MySQL, PostgreSQL, SQL Server, and SQLite.
CREATE TABLE employees ( id INTEGER PRIMARY KEY, first_name VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, salary DECIMAL(10,2) CHECK (salary >= 0), status VARCHAR(20) DEFAULT 'active', department_id INTEGER, FOREIGN KEY (department_id) REFERENCES departments(id) );
The Core Constraints
| Constraint | What it enforces |
|---|---|
| NOT NULL | This column must always have a value, never NULL |
| UNIQUE | No two rows can share the same value in this column |
| CHECK (condition) | Every value must satisfy a custom condition |
| DEFAULT value | If no value is provided on INSERT, use this one automatically |
CHECK Constraints
CHECK lets you enforce rules that go beyond type and uniqueness, a salary can never be negative, a status must be one of a known set of values, directly in the schema rather than only in application code.
salary DECIMAL(10,2) CHECK (salary >= 0),
status VARCHAR(20) CHECK (status IN ('active', 'inactive', 'on_leave'))MySQL versions before 8.0.16 (released 2019) accepted CHECK constraint syntax but silently never enforced it. Every version in common use today enforces it correctly, but it's a real historical gotcha worth knowing if you ever encounter an older MySQL instance.
Altering an Existing Table
ALTER TABLE changes a table's structure after it already exists, often after it already has data. Adding a column is well supported everywhere, but more complex changes, dropping a column, changing a column's type, used to be far more limited in SQLite specifically than in the other three databases, worth checking your SQLite version if a more advanced ALTER operation fails.
ALTER TABLE employees ADD COLUMN hire_date DATE;
ALTER TABLE employees ADD COLUMN hire_date DATE; -- SQLite supports adding a column this way, but has historically had -- limited support for other ALTER operations like dropping or renaming -- a column, improved significantly in SQLite 3.25+ (2018) and later.
Dropping a Table
DROP TABLE removes the table entirely, structure and all data, with no undo outside of a backup. IF EXISTS prevents an error if the table doesn't exist, useful in setup scripts that might run more than once.
DROP TABLE IF EXISTS employees;
Dropping an Entire Database
| DROP TABLE | DROP DATABASE | |
|---|---|---|
| Removes | One table, its structure and data | Every table in the entire database |
| Scope | Inside the database you're already connected to | The whole database itself |
| Typical use | Removing one table during development or cleanup | Tearing down an entire test or temporary database |
DROP DATABASE IF EXISTS company_db;
SQLite doesn't have a DROP DATABASE statement at all, since a SQLite "database" is just a single file, deleting that file from the filesystem is the equivalent action. On MySQL, PostgreSQL, and SQL Server, DROP DATABASE is an enormously destructive, irreversible statement, it removes every table, every row, and every view inside that database in one command, with no confirmation prompt.
All Five, Side by Side
It's worth being explicit about that last row: there's no such thing as DELETE DATABASE or TRUNCATE DATABASE in SQL. DELETE and TRUNCATE are both scoped to one table at a time, no matter how many rows or how much of it they remove. DROP DATABASE is the only statement in this course that operates on an entire database at once, which is exactly why it's the most dangerous one here.
| Statement | Removes | Structure kept? |
|---|---|---|
| DELETE FROM table WHERE ... | Specific rows you choose, in one table | Yes, the table and its unmatched rows remain |
| TRUNCATE TABLE | Every row in one table | Yes, the empty table remains |
| DROP TABLE | One table entirely | No, the table itself is gone |
| DROP DATABASE | Every table in a database | No, the whole database is gone |
| "DELETE DATABASE" / "TRUNCATE DATABASE" | Neither exists | DELETE and TRUNCATE both only ever operate on a single table, never a whole database, on any of the four databases in this course |
Frequently Asked Questions
Can I add a NOT NULL constraint to a column that already has NULL values in it?
No, every database rejects this until the existing NULLs are resolved, either by updating them to a real value first, or by adding the constraint together with a DEFAULT that backfills existing rows, depending on the database's specific ALTER TABLE syntax.
What's the difference between a column-level and table-level constraint?
A column-level constraint is written directly after that column's type, like email VARCHAR(255) UNIQUE. A table-level constraint is written as its own line, like UNIQUE (first_name, last_name), used when a constraint spans more than one column.
Does DEFAULT apply retroactively to existing rows when added later?
No, it only applies to future INSERTs that omit that column. Existing rows keep whatever value (including NULL) they already had, unless you also run an UPDATE to backfill them.
Common Beginner Mistakes
Relying only on application code to validate data, with no database-level constraints
Application code can have bugs, can be bypassed by a direct database connection, or might not be the only thing writing to the table someday. Constraints at the database level are a last line of defense that holds regardless of what wrote the data.
Assuming CHECK constraints are enforced on every MySQL version
They're silently ignored on MySQL versions before 8.0.16. If a CHECK constraint seems to be doing nothing, confirm the MySQL version first.
Running DROP TABLE without IF EXISTS in a script that might run more than once
The second run fails with a table-doesn't-exist error and stops the script. IF EXISTS makes the statement safely re-runnable.
Best Practices
- Enforce rules at the database level with constraints, don't rely solely on application code to catch bad data.
- Use CHECK for any rule that's genuinely about the data's validity, not just business logic that might change often.
- Use DEFAULT for columns that have an obvious, sensible value most of the time, like a status defaulting to 'active'.
- Always use DROP TABLE IF EXISTS and CREATE TABLE IF NOT EXISTS in setup/migration scripts meant to be re-runnable.
Interview Questions
Write a CREATE TABLE for a products table where price can't be negative and name is required and unique.
CREATE TABLE products (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, price DECIMAL(10,2) CHECK (price >= 0));
Why might a CHECK constraint silently do nothing on a production MySQL server?
If the server is running a MySQL version older than 8.0.16, CHECK constraints are parsed and accepted without error, but never actually enforced, a known historical gap in MySQL's constraint support that was fixed in that release.
What happens if you try to add a NOT NULL constraint to a column that currently contains NULL values?
The ALTER TABLE statement fails, the database can't retroactively enforce a rule that existing data already violates. The NULLs have to be resolved first, either by updating them to real values or by combining the constraint change with a default/backfill, before NOT NULL can be applied.
Summary
This lesson covered a full CREATE TABLE statement with NOT NULL, UNIQUE, CHECK, and DEFAULT constraints, a historical MySQL gotcha with CHECK, modifying an existing table with ALTER TABLE, and safely dropping a table with DROP TABLE IF EXISTS.
What's Next?
The next lesson covers indexes, how they make queries fast, and the tradeoffs that come with adding them.
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.
Both open a third-party SQL playground in a new tab. External site terms and privacy policy apply.