Skip to content

Beginner: asking questions · Databases and the relational model · topic 4 of 62 · 35 min

CREATE TABLE and constraints

Explanation

CREATE TABLE makes a new table and sets the rules (constraints) every row must follow: NOT NULL, UNIQUE, CHECK, DEFAULT, PRIMARY KEY and FOREIGN KEY. The database rejects any row that breaks a rule. ALTER TABLE changes a table later, and DROP TABLE deletes it with all its data, so use it with great care.

Why let the database check the rules?

Your app should check input, but apps have bugs, and many programs (a website, a mobile app, a night-time import script) may write to the same table. A constraint is a rule stored in the table itself. The database checks it on every insert and update, from every program, every time. Bad data never gets in.

Think of it like this: Constraints are like the form at a bank counter that the clerk checks before accepting: name filled in, signature present, amount not negative. It does not matter who brings the form; a form that breaks a rule goes back.

The shape of CREATE TABLE

sql
CREATE TABLE accounts (
  id       INTEGER PRIMARY KEY,
  holder   TEXT NOT NULL,
  email    TEXT UNIQUE,
  branch   TEXT NOT NULL DEFAULT 'Pune',
  balance  INTEGER NOT NULL DEFAULT 0 CHECK (balance >= 0)
);
Column name, type, then its constraints. Columns are separated by commas, with no comma after the last one.
ConstraintRuleError when broken (SQLite)
NOT NULLThe column must have a valueNOT NULL constraint failed: accounts.holder
UNIQUENo two rows may have the same value (NULLs are allowed)UNIQUE constraint failed: accounts.email
CHECK (condition)The condition must not be falseCHECK constraint failed: balance >= 0
DEFAULT valueUsed when an insert leaves the column outNever fails; it fills a gap
PRIMARY KEYUnique and identifies the rowUNIQUE constraint failed: ...
REFERENCES t(col)A foreign key (needs PRAGMA foreign_keys = ON in SQLite)FOREIGN KEY constraint failed

Using DEFAULT

sql
INSERT INTO accounts (id, holder, email, branch, balance)
VALUES (1, 'Asha Verma', 'asha@example.com', 'Mumbai', 15000);
INSERT INTO accounts (id, holder, email) VALUES (2, 'Ravi Kumar', 'ravi@example.com');
SELECT * FROM accounts;
-- 1 | Asha Verma | asha@example.com | Mumbai | 15000
-- 2 | Ravi Kumar | ravi@example.com | Pune   | 0
Ravi's insert leaves out branch and balance, so their DEFAULT values, Pune and 0, are used.

Notice the column list after the table name: (id, holder, email). Naming the columns in an INSERT is a good habit. The statement keeps working even if someone adds a column to the table later.

Table-level constraints

A constraint that involves several columns is written after the last column, as its own line. That is how order_items gets its composite primary key and its foreign keys.

sql
CREATE TABLE order_items (
  order_id   INTEGER NOT NULL,
  product_id INTEGER NOT NULL,
  qty        INTEGER NOT NULL CHECK (qty > 0),
  price      INTEGER NOT NULL,
  PRIMARY KEY (order_id, product_id),
  FOREIGN KEY (order_id) REFERENCES orders(id),
  FOREIGN KEY (product_id) REFERENCES products(id)
);

INTEGER PRIMARY KEY and AUTOINCREMENT in SQLite

In SQLite, a column declared exactly INTEGER PRIMARY KEY fills itself in: leave it out of the INSERT and SQLite uses the largest id so far plus one. If you delete the row with the largest id, that number can be used again.

Adding the word AUTOINCREMENT (INTEGER PRIMARY KEY AUTOINCREMENT) promises that an id is never reused, even after deletes. It costs a little extra work, and the SQLite docs advise using it only when you truly need that promise, for example when old ids are printed on receipts.

sql
-- Insert 'p' and 'q' (ids 1 and 2), delete id 2, insert 'r':
-- INTEGER PRIMARY KEY               ->  r gets id 2 (reused)
-- INTEGER PRIMARY KEY AUTOINCREMENT ->  r gets id 3 (never reused)
Other engines use different words: SERIAL or GENERATED ALWAYS AS IDENTITY in PostgreSQL, AUTO_INCREMENT in MySQL.

Changing and removing tables

sql
ALTER TABLE customers ADD COLUMN loyalty_points INTEGER NOT NULL DEFAULT 0;
DROP TABLE IF EXISTS old_imports;
ADD COLUMN adds a column to every existing row. DROP TABLE deletes the table and all its rows.

SQLite's ALTER TABLE can add, rename and drop columns and rename a table, with limits: an added column cannot be UNIQUE or a PRIMARY KEY, and a NOT NULL column needs a DEFAULT, or you get "Cannot add a NOT NULL column with default value NULL".

Careful: DROP TABLE has no undo and asks no question. One wrong name on a live database can delete years of orders. Never run it on production without a fresh backup, and use DROP TABLE IF EXISTS only in scripts that build test databases.

Common mistakes

  • A comma after the last column, or a missing comma between columns. Both are syntax errors; read the error's "near ..." part to find the spot.
  • Expecting CHECK to reject NULL. CHECK (marks > 0) passes when marks is NULL, because the result is unknown, not false. Add NOT NULL as well if the value is required.
  • Leaving out the column list in INSERT, then breaking when a column is added. Name the columns.
  • Running CREATE TABLE twice and getting "table already exists". Use CREATE TABLE IF NOT EXISTS in setup scripts.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

CREATE TABLE and constraints · 1/7

CREATE TABLE and constraints

Apps have bugs, and many programs write to one table. So we let the database itself check the rules. These rules are called constraints.

Prefer a video by a teacher? Find free videos on “sql create table constraints not null unique check default foreign key” on YouTube ↗

In real life: A wallet balance that can never go negative

A payments app keeps a wallet balance for each user. The app checks the balance before a payment, but if two payments arrive in the same instant, both checks might pass. A CHECK (balance >= 0) rule on the table is the last line of defence: the database itself refuses any update that would make a balance negative, whichever server or script sent it.

sql
CREATE TABLE wallets (
  user_id  INTEGER PRIMARY KEY,
  balance  INTEGER NOT NULL DEFAULT 0 CHECK (balance >= 0)  -- in paise
);
Storing paise as an INTEGER keeps money exact.

Practice (0/3 done)

Task 1 · Code · SQL

The database is empty. 1) Create a table accounts with columns id (INTEGER PRIMARY KEY), holder (TEXT NOT NULL), branch (TEXT NOT NULL DEFAULT 'Pune') and balance (INTEGER NOT NULL DEFAULT 0 CHECK (balance >= 0)). 2) Insert (1, 'Asha Verma', 'Mumbai', 15000) naming all four columns. 3) Insert account 2 for 'Ravi Kumar' giving ONLY id and holder, so the defaults are used. 4) SELECT id, holder, branch, balance FROM accounts.

Task 2 · Code · SQL

Create a table results with columns roll (INTEGER PRIMARY KEY), name (TEXT NOT NULL) and marks (INTEGER CHECK (marks BETWEEN 0 AND 100)). You are given four rows: (1, 'Asha', 76), (2, 'Ravi', 67), (3, 'Karan', 105), (4, 'Imran', 0). One of them breaks the CHECK rule; try it and read the error. Then insert ONLY the valid rows and finish with SELECT roll, name, marks FROM results.

Task 3 · Code · SQL

The SHOP owner starts a loyalty scheme. Add a column loyalty_points (INTEGER NOT NULL DEFAULT 0) to customers with ALTER TABLE, then return id, name and loyalty_points of every customer.

Quick quiz

Question 1

1.A column is declared marks INTEGER CHECK (marks >= 0). Which insert is accepted?

Question 2

2.What does DEFAULT 0 do?

Question 3

3.In SQLite, what is the difference between INTEGER PRIMARY KEY and INTEGER PRIMARY KEY AUTOINCREMENT?

Question 4

4.Which statement deletes a table and all its rows, with no undo?

Question 5

5.Why add constraints in the database when the app already checks the input?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A column is declared marks INTEGER CHECK (marks >= 0). Which insert is accepted?

Answer: marks = NULL

CHECK fails only when the condition is false. With NULL it is unknown, so the row is accepted. Add NOT NULL to block it.

What does DEFAULT 0 do?

Answer: Puts 0 in the column when an INSERT leaves it out

DEFAULT supplies a value only when the insert does not give one.

In SQLite, what is the difference between INTEGER PRIMARY KEY and INTEGER PRIMARY KEY AUTOINCREMENT?

Answer: AUTOINCREMENT never reuses an id, even after deletes

Both fill in ids. Without AUTOINCREMENT, the id of a deleted last row can be used again.

Which statement deletes a table and all its rows, with no undo?

Answer: DROP TABLE

DROP TABLE removes the table itself. Always have a backup before using it on real data.

Why add constraints in the database when the app already checks the input?

Answer: Every program that writes to the table is checked, including buggy ones

Many programs may write to one table. Constraints protect the data from all of them.

A column is left out of an INSERT and has no DEFAULT and no NOT NULL. What value does it get?Related

Answer: NULL

With no default, a missing column becomes NULL.

From INSERT

In SQLite, foreign keys are declared but you can still insert an order for customer 99, who does not exist. Why?Related

Answer: Foreign key checks are off until PRAGMA foreign_keys = ON

SQLite enforces foreign keys only after the PRAGMA, once per connection.

From Keys and relationships

Which statement adds a new row?Related

Answer: INSERT INTO

INSERT INTO table (columns) VALUES (...) adds rows.

From INSERT, UPDATE and DELETE

What is special about a primary key?Related

Answer: Its value is unique for every row

A primary key identifies exactly one row, like a roll number or a PNR.

From What is a database?

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Databases and the relational model” before its exam: The SQL language and your tools.
  2. Module 1 exam: Databases and the relational model · 15 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, SELECT: filtering and sorting. 62 topics left in the course before the final exam.

← Previous: Keys and relationships