انتقل إلى المحتوى
أمين أبوزيد
منشورBEG-050-L02

CREATE وALTER

  • الدرس 2 من 9
  • 12 دقيقة قراءة
  • مبتدئ
في هذا الدرس 11 موضوعات

هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.

Lesson Overview

Lesson 1 split SQL into DDL (structure) and DML (rows). This lesson is the first half of DDL: building the shelves. You will turn the tables you designed in BEG-049 into real tables with CREATE TABLE, and change them later with ALTER TABLE. You will also meet DROP TABLE, the statement that removes a shelf together with every book on it.

In the library picture, CREATE TABLE is the carpenter's order: "one shelf called books, with slots for a title, an author, and a year." The database will refuse anything that does not fit those slots, which is exactly why they are worth getting right.

By the end you will have the four-table library schema (authors, books, members, loans) that the rest of the module queries.

This lesson builds on Lesson 1 (what DDL is) and BEG-049 (tables, primary keys, foreign keys).

Learning Objectives

By the end of this lesson you will be able to:

  • Write a CREATE TABLE statement with column names, types, and a primary key.
  • Choose a sensible type for numbers, text, dates, and yes/no values.
  • Use NOT NULL, DEFAULT, and REFERENCES (a foreign key) in a column definition.
  • Create tables in an order that lets foreign keys work.
  • Add, rename, and drop a column with ALTER TABLE, and predict when adding a column fails.
  • Explain what DROP TABLE does and why it is dangerous.

Written Explanation

CREATE TABLE

The general shape:

sql
CREATE TABLE table_name (
  column_name  type  [constraints],
  column_name  type  [constraints],
  ...
);

Each column gets a name, a type, and optional constraints: rules the database enforces. Columns are separated by commas, and there is no comma after the last one.

A first complete example, from the library:

sql
CREATE TABLE authors (
  id      INTEGER PRIMARY KEY,
  name    TEXT NOT NULL,
  country TEXT
);

Reading it line by line: id holds whole numbers and is the primary key (unique, never empty); name holds text and must be filled in; country holds text and may be empty (NULL).

Types you need now

TypeHoldsLibrary example
INTEGERWhole numberspublished_year, ids
TEXTText of any lengthtitle, name
DATEA calendar dateloaned_on, due_on
BOOLEANtrue or falseactive
NUMERIC(p, s)Exact decimalsA fine of 1.50

PostgreSQL has many more types (VARCHAR(n) for text with a length limit, TIMESTAMP for date and time, BIGINT for large whole numbers). Other databases name and treat some types differently, so check your product. Choose the type that matches the meaning: a year is a number, a date is a DATE, never a text like '4 May', because the database can then compare and sort it correctly.

Constraints you need now

ConstraintMeaning
PRIMARY KEYUnique and not empty: identifies one row (BEG-049)
NOT NULLThe column must always have a value
DEFAULT valueThe value used when an INSERT leaves the column out
REFERENCES table (column)A foreign key: values must exist in that other table's column

An example with a foreign key:

sql
CREATE TABLE books (
  id             INTEGER PRIMARY KEY,
  title          TEXT NOT NULL,
  author_id      INTEGER NOT NULL REFERENCES authors (id),
  published_year INTEGER
);

author_id can only hold an id that exists in authors. That is what keeps a book from pointing at an author who does not exist.

Order matters. A foreign key points at a table that must already exist, so create the "one" side before the "many" side: authors before books, and books and members before loans.

ALTER TABLE

Requirements change after a table is created. ALTER TABLE changes the definition without recreating the table. The PostgreSQL manual lists these operations: add columns, remove columns, add and remove constraints, change default values, change data types, rename columns, and rename tables. The common ones:

sql
ALTER TABLE publishers ADD COLUMN website TEXT;
ALTER TABLE publishers RENAME COLUMN city TO hq_city;
ALTER TABLE publishers DROP COLUMN founded;

Adding a column to a table that already has rows. The new column appears in every existing row. It holds the DEFAULT if you gave one, otherwise NULL. That has a consequence: ADD COLUMN ... NOT NULL with no default cannot work on a table that already holds rows, because the existing rows would have to be NULL. The fix is to give a default (or to add the column as nullable, fill it with UPDATE, then add NOT NULL).

DROP COLUMN deletes that column's data in every row. Nothing asks "are you sure".

DROP TABLE

sql
DROP TABLE publishers;

This removes the table, its structure, and all of its rows. It is the DDL statement to treat with the most respect. PostgreSQL will refuse if another table's foreign key still depends on the target, and says so; the hint offers CASCADE, which also removes the dependent constraints. Do not reach for CASCADE just to make an error go away: read what it would remove first.

CREATE TABLE IF NOT EXISTS and DROP TABLE IF EXISTS skip the work (with a notice) instead of raising an error. They are handy in setup scripts, but they can also hide the fact that a table was not what you thought it was.

Examples

All examples were run on PostgreSQL 18.3 with psql.

Example 1: the library schema (simple)

sql
CREATE TABLE authors (
  id      INTEGER PRIMARY KEY,
  name    TEXT NOT NULL,
  country TEXT
);
CREATE TABLE books (
  id             INTEGER PRIMARY KEY,
  title          TEXT NOT NULL,
  author_id      INTEGER NOT NULL REFERENCES authors (id),
  published_year INTEGER
);
CREATE TABLE members (
  id        INTEGER PRIMARY KEY,
  name      TEXT NOT NULL,
  joined_on DATE NOT NULL
);
CREATE TABLE loans (
  id          INTEGER PRIMARY KEY,
  book_id     INTEGER NOT NULL REFERENCES books (id),
  member_id   INTEGER NOT NULL REFERENCES members (id),
  loaned_on   DATE NOT NULL,
  due_on      DATE NOT NULL,
  returned_on DATE
);

Each statement answered CREATE TABLE. Note that returned_on is the only column without NOT NULL on loans: a book that is still out has no return date yet, and NULL is how we record that. This is the schema from BEG-049 (books, authors, members, loans) written as SQL, and the rest of the module uses it.

Example 2: growing a table with ALTER (practical)

A new requirement arrives: the library wants to record publishers. Create the table, then extend it.

sql
CREATE TABLE publishers (
  id   INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  city TEXT
);
ALTER TABLE publishers ADD COLUMN website TEXT;

psql's \d publishers command (a psql feature, not SQL) describes the table:

text
 Column  |  Type   | Collation | Nullable | Default 
---------+---------+-----------+----------+---------
 id      | integer |           | not null | 
 name    | text    |           | not null | 
 city    | text    |           |          | 
 website | text    |           |          | 
Indexes:
    "publishers_pkey" PRIMARY KEY, btree (id)

PostgreSQL created an index to enforce the primary key, which you can ignore for now. After a row exists, we try more changes:

sql
INSERT INTO publishers (id, name, city) VALUES (1, 'Orbit Press', 'Cairo');
ALTER TABLE publishers ADD COLUMN founded INTEGER DEFAULT 2000;
SELECT * FROM publishers;
text
 id |    name     | city  | website | founded 
----+-------------+-------+---------+---------
  1 | Orbit Press | Cairo |         |    2000
(1 row)

The existing row received the default, 2000, and its website is NULL. Next, NOT NULL with no default:

sql
ALTER TABLE publishers ADD COLUMN email TEXT NOT NULL;
text
ERROR:  column "email" of relation "publishers" contains null values

The row already exists, so the new column would have to be NULL there. The version with a default works:

sql
ALTER TABLE publishers ADD COLUMN active BOOLEAN NOT NULL DEFAULT TRUE;
ALTER TABLE publishers RENAME COLUMN city TO hq_city;
ALTER TABLE publishers DROP COLUMN founded;
SELECT * FROM publishers;
text
 id |    name     | hq_city | website | active 
----+-------------+---------+---------+--------
  1 | Orbit Press | Cairo   |         | t
(1 row)

The t is how psql displays true.

Example 3: errors you will meet and DROP (realistic)

Creating a table whose foreign key points to something that does not exist, then dropping a parent table that others depend on:

sql
CREATE TABLE copies (id INTEGER PRIMARY KEY, book_id INTEGER REFERENCES nobooks (id));
DROP TABLE authors;
text
ERROR:  relation "nobooks" does not exist
ERROR:  cannot drop table authors because other objects depend on it
DETAIL:  constraint books_author_id_fkey on table books depends on table authors
HINT:  Use DROP ... CASCADE to drop the dependent objects too.

Both errors protect you: the first stops a foreign key from pointing at nothing, the second stops you from orphaning books. Repeating a drop shows the IF EXISTS behaviour:

sql
DROP TABLE publishers;
DROP TABLE publishers;
DROP TABLE IF EXISTS publishers;
text
DROP TABLE
ERROR:  table "publishers" does not exist
NOTICE:  table "publishers" does not exist, skipping
DROP TABLE

Backend Insight

In a real project the CREATE TABLE statements live in files that are saved in version control, not typed into a console. They are the record of what the database should look like, and they are applied the same way on every developer's machine and on the server. That is also why ALTER is usually written as a numbered script ("add publishers table", "add website column") rather than edited by hand: each change can be reviewed, repeated, and traced.

Types and constraints are your first line of defence. A NOT NULL on name and a foreign key on author_id mean the database itself rejects bad data, even if a bug in the application code tries to save it. Constraints written in the schema protect every program that ever connects, not just the one you are writing today.

Common Mistakes

Mistake 1: A trailing comma or a missing comma

A comma after the last column, or a missing comma between two columns, gives a syntax error pointing near the spot. Check the line above the one the error names.

Mistake 2: Creating tables in the wrong order

REFERENCES needs the other table to exist already, as in Example 3. Create parents first, or add the foreign key later with ALTER TABLE.

Mistake 3: Adding NOT NULL without a default on a table with rows

It fails if any rows exist. Use a DEFAULT, or add the column as nullable, fill it with UPDATE, then add NOT NULL.

Mistake 4: Storing dates or numbers as TEXT

A TEXT year sorts '1000' before '999', and a text date cannot be compared reliably. Use INTEGER and DATE.

Mistake 5: Using DROP to "start over" on a real database

DROP TABLE and DROP COLUMN destroy data with no confirmation. On practice databases it is a fine way to reset; on a real one, it is an incident.

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. Write CREATE TABLE for shelves with id (integer primary key), label (text, required), and floor (integer, optional).

أظهر الحل A1

A1.

sql
CREATE TABLE shelves (
  id    INTEGER PRIMARY KEY,
  label TEXT NOT NULL,
  floor INTEGER
);

A2. Which type would you choose for: (a) a member's birthday, (b) the number of pages, (c) whether a book is a reference copy, (d) a book title?

أظهر الحل A2

A2. (a) DATE. (b) INTEGER. (c) BOOLEAN. (d) TEXT (or VARCHAR(n)).

A3. What does NOT NULL do? What does DEFAULT 0 do?

أظهر الحل A3

A3. NOT NULL makes the column required: an insert or update that leaves it empty is rejected. DEFAULT 0 supplies 0 whenever an insert omits the column.

Level B: Application

B1. Create reviews with id, book_id (must exist in books), rating (integer, required), and comment (optional text). In which order relative to books do you create it?

أظهر الحل B1

B1.

sql
CREATE TABLE reviews (
  id      INTEGER PRIMARY KEY,
  book_id INTEGER NOT NULL REFERENCES books (id),
  rating  INTEGER NOT NULL,
  comment TEXT
);

Create it after books, because the foreign key needs books to exist.

B2. publishers already contains rows. Add a text column country that is required, defaulting to 'Unknown'. Then say what the existing rows hold.

أظهر الحل B2

B2. ALTER TABLE publishers ADD COLUMN country TEXT NOT NULL DEFAULT 'Unknown'; Existing rows hold 'Unknown'.

B3. Rename shelves.label to title, then remove the floor column. What happens to the data in floor?

أظهر الحل B3

B3.

sql
ALTER TABLE shelves RENAME COLUMN label TO title;
ALTER TABLE shelves DROP COLUMN floor;

The floor data is deleted from every row with the column.

Level C: Challenge

C1. members has 500 rows. You must add a required email column. Describe a safe sequence of steps that never needs a fake default to stay forever.

أظهر الحل C1

C1. (1) ALTER TABLE members ADD COLUMN email TEXT; (nullable, so existing rows get NULL). (2) Fill in real emails with UPDATE, from a source such as the application or a spreadsheet. (3) Once no NULL remains, run ALTER TABLE members ALTER COLUMN email SET NOT NULL;, which the manual says checks existing data immediately.

C2. A teammate runs DROP TABLE authors; on a database where books has a foreign key to it, gets an error with a CASCADE hint, and wants to add CASCADE "to fix it". What do you tell them, and what do you check first?

أظهر الحل C2

C2. Do not add it blindly. The error is protecting books. First read the DETAIL line to see what depends on authors, and ask whether the table really should be dropped at all. CASCADE removes the dependent foreign-key constraints, which changes books' guarantees. If the drop is intended, preserve a backup and make the change deliberately.

Mini Challenge

Design and create a fines table for the library: each fine belongs to one loan, has an amount (exact decimal, required), a paid flag that defaults to false, and an optional note. Then, after creating it, add a fined_on date column that is required and defaults to the date '2025-01-01' for any old rows.

Write the statements, run them if you have a PostgreSQL client available, and describe the table with your client's table-describing feature (menus and commands differ between tools).

Check your work: loan_id references loans (id) and loans was created first; the amount uses NUMERIC; paid is BOOLEAN NOT NULL DEFAULT FALSE; fined_on was added with ALTER TABLE ... ADD COLUMN.

Summary & Next Lesson

Key takeaways

  • CREATE TABLE defines columns with a name, a type, and optional constraints.
  • Choose types by meaning: INTEGER, TEXT, DATE, BOOLEAN, NUMERIC.
  • PRIMARY KEY, NOT NULL, DEFAULT, and REFERENCES make the database enforce your design.
  • Create parent tables before the tables that reference them.
  • ALTER TABLE adds, renames, and drops columns; adding NOT NULL without a default fails when rows already exist.
  • DROP TABLE and DROP COLUMN delete data permanently.

Next lesson

The shelves exist but are empty. The next lesson, "INSERT, UPDATE, DELETE", moves to DML: adding, changing, and removing rows, and the WHERE habit that keeps those changes safe.

Self-check

  1. Which part of a column definition says it must always have a value?
  2. Why create authors before books?
  3. What happens to existing rows when you add a column with DEFAULT 'Unknown'?
  4. Why does ADD COLUMN email TEXT NOT NULL fail on a table with rows?
  5. What does DROP TABLE remove?
أظهر الإجابات
  1. NOT NULL.
  2. books has a foreign key to authors, which must exist first.
  3. They all receive 'Unknown'.
  4. Existing rows would need a value for the new column, but none is given, so they would be NULL.
  5. The table, its structure, and every row in it.

References

  • PostgreSQL documentation, Modifying Tables. Supports the list of ALTER TABLE operations, new columns taking the default or NULL, and SET NOT NULL being checked against existing data.
  • PostgreSQL documentation, Data Types. Supports the type names integer, numeric, boolean, date, timestamp, text, and varchar(n).
  • All statements and error messages were produced by running them on PostgreSQL 18.3. The advice about migration scripts is general practice, not taken from a cited page.

التطبيق

طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.

المختبر والتمارين الاختبار

تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.

اكتب للبحث في المستويات والمراحل والوحدات والدروس.