SQL • Data Definition and Manipulation

ALTER TABLE

Evolving database schemas in place without destroying existing data or disrupting live applications.

SQL illustration with a database, structured table, and query code symbols
Inside this lesson 10 sections 5 questions 9 figures

SECTION 1

Historical Context & Motivation

Relational databases emerged from E.F. Codd's seminal 1970 paper proposing a mathematical model for data storage based on set theory and first-order predicate logic. Early implementations, however, treated table schemas as essentially immutable artifacts—once a table was defined, changing its structure often required dropping it entirely, exporting the data, and rebuilding from scratch. As production systems grew in scale and complexity, this destructive workflow became untenable. The ALTER TABLE statement was introduced to solve precisely this problem: it allows a database administrator or developer to modify the structure of an existing table—adding columns, changing data types, imposing constraints—without losing the data already stored within it. This capability transformed schema management from a disruptive, batch-oriented process into a routine, incremental operation compatible with the demands of continuously running applications.

1970 • Codd's Relational Model

Edgar F. Codd publishes "A Relational Model of Data for Large Shared Data Banks," establishing the theoretical foundation for relational databases and the concept of structured table definitions.

1974 • System R & SEQUEL

IBM's System R prototype introduces SEQUEL (later renamed SQL), including early DDL statements like CREATE TABLE. Schema modification capabilities remain limited; structural changes typically require table recreation.

1986 • SQL-86 (ANSI Standard)

The first ANSI/ISO SQL standard is ratified, formalizing ALTER TABLE as part of the Data Definition Language. The standard specifies ADD COLUMN as a core operation, giving vendors a portable syntax for schema evolution.

1992 • SQL-92 Expansion

SQL-92 significantly broadens ALTER TABLE to include DROP COLUMN, ALTER COLUMN for data type changes, and constraint management (ADD/DROP CONSTRAINT), establishing the modern ALTER TABLE vocabulary used across major RDBMS platforms.

2010s • Online DDL & Migrations

Major databases (MySQL 5.6+, PostgreSQL 11+, SQL Server Online Index Operations) introduce online or non-blocking ALTER TABLE operations, enabling schema changes on tables serving millions of concurrent requests without downtime.

The central question ALTER TABLE addresses is deceptively straightforward: how can we evolve a database schema to meet changing application requirements while preserving referential integrity, minimizing downtime, and avoiding data loss? Understanding the syntax, semantics, and operational implications of ALTER TABLE is essential for any computer science professional who interacts with relational systems—whether designing migration scripts, managing production deployments, or reasoning about database performance under structural change.

SECTION 2

Core Principles & Definitions

ALTER TABLE belongs to the Data Definition Language (DDL) subset of SQL, alongside CREATE, DROP, and TRUNCATE. While DML statements (INSERT, UPDATE, DELETE, SELECT) operate on the data within tables, DDL statements operate on the metadata that defines table structure—column names, data types, constraints, indexes, and relationships. ALTER TABLE is unique among DDL statements because it modifies existing structures rather than creating or destroying them. This makes it the primary tool for schema evolution, the process of incrementally adapting a database's logical model over time.

1

Non-Destructive Modification

ALTER TABLE changes the schema of an existing table without dropping and recreating it. Existing rows are preserved, and the operation modifies the table's catalog entry in the system metadata.
2

Atomicity of DDL

In most RDBMS implementations, each ALTER TABLE statement is atomic—it either completes fully or rolls back entirely. Some systems (PostgreSQL) wrap DDL in transactions; others (MySQL) issue implicit commits.
3

Constraint Enforcement

Adding or removing constraints (PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, NOT NULL) through ALTER TABLE enforces data integrity rules retroactively on all existing rows, potentially causing failures if violations exist.
4

Locking & Concurrency

Many ALTER TABLE operations acquire exclusive table locks, blocking concurrent reads and writes. Understanding lock behavior is critical for production systems where availability requirements are stringent.
5

Vendor-Specific Extensions

While the ANSI SQL standard defines core ALTER TABLE syntax, each RDBMS (PostgreSQL, MySQL, SQL Server, Oracle) extends it with proprietary clauses for partitioning, storage parameters, and online operations.
✦

KEY TAKEAWAY

Think of ALTER TABLE as renovating a building while tenants continue to live in it. CREATE TABLE is the original construction, DROP TABLE is demolition, but ALTER TABLE is the remodeling—you can add rooms (columns), reinforce walls (constraints), or change the plumbing (data types) without forcing everyone to vacate. The skill lies in knowing which renovations can happen with tenants present and which require a brief evacuation (downtime).

SECTION 3

Visual Explanation: ALTER TABLE Operation Flow

The following diagram illustrates the lifecycle of an ALTER TABLE statement as it moves from the SQL parser through the catalog manager, constraint validator, and storage engine. Each stage plays a critical role in ensuring that the structural modification is both syntactically valid and semantically consistent with the existing data and constraints.

ALTER TABLE Execution PipelineSQL ParserSyntax validationCatalog ManagerResolve table metadataLock AcquisitionExclusive or shared lockConstraint CheckValidate existing dataStorage EngineApply physical changesCatalog UpdatePersist new schemaLock ReleaseResume concurrencyCommon ALTER TABLE OperationsADD COLUMN→ Appends column; NULLs for existing rowsDROP COLUMN→ Removes column and its data permanentlyALTER COLUMN→ Changes type, default, or nullabilityADD CONSTRAINT→ Enforces rule on all existing + future rowsRENAME→ Changes table or column identifier
The ALTER TABLE execution pipeline: from parsing and catalog resolution through locking, constraint validation, storage engine modification, catalog update, and finally lock release. The right panel summarizes the five most common operation types.

As the diagram illustrates, the SQL Parser first validates the syntax of the ALTER TABLE statement before the Catalog Manager resolves the target table's metadata from the system catalog. An exclusive lock is then acquired to prevent concurrent modifications from corrupting the schema. The Constraint Check stage is particularly important when adding NOT NULL or CHECK constraints—the engine must verify that every existing row satisfies the new rule before committing. Only after validation does the Storage Engine apply physical changes (rewriting pages, adjusting indexes), followed by the catalog update that makes the new schema visible to subsequent queries.

SECTION 4

Syntax & Mechanics Deep Dive

The general syntax of ALTER TABLE follows a predictable pattern across RDBMS implementations, though vendor-specific extensions add nuance. The ANSI SQL standard defines the core grammar, and understanding this grammar formally enables you to reason about any vendor's dialect. Below, we present the canonical forms of the most frequently used operations.

ADD COLUMN

ADD COLUMN SYNTAX

ALTERTABLEtablenameADD[COLUMN]columnnamedatatype[constraint];ALTER TABLE table_name ADD [COLUMN] column_name data_type [constraint];

Where table_name is the target relation, column_name is the new attribute identifier, data_type specifies the domain (e.g., VARCHAR(255), INTEGER, TIMESTAMP), and constraint is an optional inline constraint such as NOT NULL, DEFAULT, or UNIQUE.

DROP COLUMN

DROP COLUMN SYNTAX

ALTERTABLEtablenameDROP[COLUMN]columnname[CASCADE∣RESTRICT];ALTER TABLE table_name DROP [COLUMN] column_name [CASCADE | RESTRICT];

CASCADE automatically drops dependent objects (views, indexes, foreign keys referencing the column). RESTRICT (the default in most systems) causes the statement to fail if any dependencies exist, preventing accidental data loss.

ALTER COLUMN / MODIFY COLUMN

ALTER COLUMN SYNTAX (ANSI / POSTGRESQL)

ALTERTABLEtablenameALTERCOLUMNcolumnnameSETDATATYPEnewtype;ALTER TABLE table_name ALTER COLUMN column_name SET DATA TYPE new_type;

MySQL uses MODIFY COLUMN column_name new_type instead. Type changes may require implicit or explicit casting of existing data; widening conversions (INT → BIGINT) typically succeed, while narrowing conversions (VARCHAR(255) → VARCHAR(50)) may fail if existing values exceed the new limit.

ADD / DROP CONSTRAINT

ADD CONSTRAINT SYNTAX

ALTERTABLEtablenameADDCONSTRAINTconstraintnameconstraintdefinition;ALTER TABLE table_name ADD CONSTRAINT constraint_name constraint_definition;

Constraint definitions include PRIMARY KEY (col), FOREIGN KEY (col) REFERENCES other_table(col), UNIQUE (col), and CHECK (expression). Naming constraints explicitly with constraint_name is a best practice that simplifies future DROP CONSTRAINT operations.

⚠

Transactional DDL Varies by RDBMS

PostgreSQL wraps ALTER TABLE in the current transaction, meaning you can roll back schema changes. MySQL's InnoDB issues an implicit COMMIT before and after DDL statements, making them non-transactional. Oracle behaves similarly to MySQL. Always consult your RDBMS documentation when scripting multi-step migrations.

SECTION 5

Detailed Breakdown of ALTER TABLE Operations

ALTER TABLE operations can be classified along two dimensions: the structural target (columns, constraints, table properties) and the impact level (metadata-only changes versus full table rewrites). This classification is critical for production systems because metadata-only changes are nearly instantaneous, while table rewrites lock the table and may take minutes or hours on large datasets. The diagram below maps common operations into these categories.

ALTER TABLE Operation Classification MatrixMetadata-Only (Fast)Table Rewrite (Slow)ColumnsConstraints● ADD COLUMN (nullable, no default) Only updates catalog; no row scan needed● RENAME COLUMN / TABLE Identifier change in system catalog only● SET / DROP DEFAULT● ADD COLUMN with DEFAULT (pre-PG 11) Must backfill default into every existing row● ALTER COLUMN TYPE (narrowing) Each row must be validated and rewritten● DROP COLUMN (some engines)● DROP CONSTRAINT Removes rule from catalog; no data scan● ADD CONSTRAINT (NOT VALID, PG) Deferred validation; catalog-only initially● RENAME CONSTRAINT● ADD CONSTRAINT (with validation) Full table scan to verify all rows● ADD PRIMARY KEY / UNIQUE Requires index creation; scans all rows● ADD FOREIGN KEY Validates referential integrity across tables⚡ Metadata-only operations complete in O(1) time regardless of table size.🐢 Table-rewrite operations scale with O(n) where n = number of rows.
The 2×2 classification matrix organizes ALTER TABLE operations by structural target (columns vs. constraints) and performance impact (metadata-only vs. table rewrite). Green quadrants are fast; red and amber quadrants require careful planning in production.

The distinction between metadata-only and table-rewrite operations has profound practical consequences. On a table with one billion rows, a metadata-only operation like ADD COLUMN email VARCHAR(255) (nullable, no default) completes in milliseconds on PostgreSQL because it merely appends a column definition to the system catalog. By contrast, ALTER COLUMN id SET DATA TYPE BIGINT on the same table must read, convert, and rewrite every single row—a process that could take hours and hold an exclusive lock for the duration. Modern RDBMS versions have progressively moved more operations into the metadata-only category (PostgreSQL 11, for example, made ADD COLUMN with a non-volatile DEFAULT metadata-only), but a CS professional must always verify the behavior for their specific database version before executing ALTER TABLE on production data.

SECTION 6

Worked Example: Evolving an E-Commerce Schema

Consider a production e-commerce database with a products table. The original schema was created with the following DDL:

ORIGINAL TABLE DEFINITION

CREATETABLEproducts(idSERIALPRIMARYKEY,nameVARCHAR(100)NOTNULL,priceDECIMAL(8,2)NOTNULL,categoryVARCHAR(50));CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, price DECIMAL(8,2) NOT NULL, category VARCHAR(50) );

A minimal product catalog with an auto-incrementing primary key, product name, price, and optional category.

Business requirements have evolved: we need to track product weight for shipping calculations, enforce that prices are positive, add a foreign key to a new categories table, rename the legacy category column, and widen the name column to accommodate longer product titles. Let's walk through each migration step.

Multi-Step Schema Migration

  1. 1

    Step 1 — Add a Nullable Column for Weight

    We add a weight_kg column with a DECIMAL type to store weight in kilograms. Since we cannot immediately populate values for existing products, we leave it nullable—this is a metadata-only operation and executes instantly.
    ALTER TABLE products ADD COLUMN weight_kg DECIMAL(6,3);
  2. 2

    Step 2 — Add a CHECK Constraint on Price

    We enforce a business rule that prices must be strictly positive. The RDBMS will scan all existing rows to verify compliance before committing. If any row has a price ≤ 0, the statement fails and the constraint is not added.
    ALTER TABLE products ADD CONSTRAINT chk_positive_price CHECK (price > 0);
  3. 3

    Step 3 — Rename the Legacy Category Column

    Before introducing a foreign key to a normalized categories table, we rename the old free-text column to clearly mark it as legacy data. This is a metadata-only operation and does not affect stored values, but application code referencing the old column name will break—coordinate with deployment.
    ALTER TABLE products RENAME COLUMN category TO legacy_category;
  4. 4

    Step 4 — Add a Foreign Key Column and Constraint

    We add a category_id integer column referencing the categories table. We perform this in two statements: first add the nullable column (metadata-only), then add the foreign key constraint. Note that the FK constraint will validate existing rows—since all values are NULL and NULLs satisfy FK checks, this succeeds immediately.
    ALTER TABLE products ADD COLUMN category_id INTEGER; ALTER TABLE products ADD CONSTRAINT fk_product_category FOREIGN KEY (category_id) REFERENCES categories(id);
  5. 5

    Step 5 — Widen the Name Column

    We expand name from VARCHAR(100) to VARCHAR(255). In PostgreSQL, widening a VARCHAR column is a metadata-only operation (it simply updates the type modifier in the catalog). In MySQL with InnoDB, this may trigger a table rebuild depending on the version and row format.
    ALTER TABLE products ALTER COLUMN name SET DATA TYPE VARCHAR(255);
💡

Migration Best Practice

In production environments, these steps would typically be captured in a migration tool (Flyway, Liquibase, Alembic, or Rails ActiveRecord Migrations) as versioned, idempotent scripts. Each migration is tested against a staging environment before being applied to production, and the tool tracks which migrations have been executed via a metadata table.

SECTION 7

Strengths, Limitations & Vendor Comparison

While ALTER TABLE is indispensable for schema evolution, its behavior varies significantly across major RDBMS platforms. Understanding these differences is essential for writing portable migration scripts and planning zero-downtime deployments. The table below compares four major platforms across key dimensions.

FeaturePostgreSQLMySQL (InnoDB)SQL Server
Transactional DDL✅ Full transaction support; DDL can be rolled back❌ Implicit COMMIT before and after DDL✅ DDL participates in explicit transactions
ADD COLUMN (nullable)Metadata-only; instantINSTANT algorithm (8.0+) for most typesMetadata-only; instant
ADD COLUMN with DEFAULTMetadata-only since v11; stored in catalogINSTANT for some types (8.0.12+); otherwise INPLACE rewriteMetadata-only since SQL Server 2012 (Enterprise)
Online ALTER (no lock)CREATE INDEX CONCURRENTLY; limited for column opsALGORITHM=INPLACE, LOCK=NONE for many operationsONLINE = ON for index operations
DROP COLUMNMarks column as dropped; space reclaimed on VACUUMINSTANT in 8.0.29+; otherwise full rebuildMetadata-only if no constraints depend on it
Multiple operations in one statementYes; comma-separated sub-commandsYes; comma-separated sub-commandsLimited; usually one operation per statement

ALTER TABLE behavior comparison across PostgreSQL, MySQL (InnoDB), and SQL Server

✦

KEY TAKEAWAY

ALTER TABLE is like upgrading a plane's engine while it's flying—the SQL standard gives you the blueprint, but each airline (RDBMS vendor) has different procedures for performing the swap. PostgreSQL lets you pause mid-upgrade and reverse course (transactional DDL), MySQL commits each change irrevocably the moment it starts, and SQL Server falls somewhere in between. Before executing any ALTER TABLE in production, always consult the specific locking, algorithm, and transaction behavior of your exact database version.

SECTION 8

Connection to Advanced Schema Management

ALTER TABLE is the foundational building block of schema migration frameworks, but production-scale database management introduces additional complexity that transcends individual DDL statements. At the advanced level, you will encounter techniques such as blue-green deployments (where two parallel schemas coexist during transition), expand-contract migrations (which break destructive changes into safe, incremental phases), and ghost table migrations (used by tools like gh-ost and pt-online-schema-change to perform ALTER TABLE without locking the original table). These advanced patterns all rely on ALTER TABLE internally but add orchestration layers to achieve zero-downtime guarantees.

ConceptALTER TABLE (Standard)Advanced Pattern
Column AdditionALTER TABLE t ADD COLUMN c TYPE;Expand-contract: add nullable column, deploy dual-write code, backfill, add NOT NULL, remove old code path
Column RemovalALTER TABLE t DROP COLUMN c;Contract phase: stop reading column in app, deploy, then DROP COLUMN as a separate migration
Type ChangeALTER TABLE t ALTER COLUMN c SET DATA TYPE ...;Ghost table: create shadow table with new type, replicate changes via triggers/binlog, atomic rename swap
Constraint AdditionALTER TABLE t ADD CONSTRAINT ...;Two-phase: ADD CONSTRAINT ... NOT VALID (PG), then VALIDATE CONSTRAINT in a separate, non-blocking step
Index CreationImplicit index with ADD PRIMARY KEY / UNIQUECREATE INDEX CONCURRENTLY (PG) or ALGORITHM=INPLACE, LOCK=NONE (MySQL)

Standard ALTER TABLE operations versus advanced zero-downtime migration patterns

As you progress into database engineering and DevOps coursework, you will also encounter the concept of schema versioning—treating your database schema as code that lives in version control alongside your application. Tools like Flyway assign monotonically increasing version numbers to migration scripts (V1__create_products.sql, V2__add_weight_column.sql), and each script typically contains one or more ALTER TABLE statements. This approach makes schema evolution reproducible, auditable, and reversible—qualities that are non-negotiable in enterprise software development.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain why adding a nullable column with no default value is typically a metadata-only operation, while adding a column with a NOT NULL constraint and a default value may require a full table rewrite on older database versions. What changed in PostgreSQL 11 that made the latter case metadata-only as well?
Show answer
When a nullable column with no default is added, the database simply updates the system catalog to reflect the new column definition. Existing rows do not need to be modified because any read of the new column for an old row implicitly returns NULL. However, a NOT NULL column with a default value traditionally required the database to physically write the default value into every existing row to satisfy the NOT NULL constraint, resulting in an O(n) table rewrite. PostgreSQL 11 introduced a catalog-level optimization: the default value is stored in the pg_attribute system catalog rather than being materialized in each row. When a query reads a row that predates the ALTER TABLE, the executor substitutes the catalog-stored default, avoiding any physical rewrite while still satisfying NOT NULL semantics.

Question 2: BASIC

Given a table students(id INT PRIMARY KEY, name VARCHAR(50), gpa DECIMAL(3,2)), write a single ALTER TABLE statement that adds a column enrollment_date of type DATE with a default value of '2025-01-01'.
Show answer
The correct statement is: ALTER TABLE students ADD COLUMN enrollment_date DATE DEFAULT '2025-01-01'; This adds the column and assigns the specified default to all existing rows (either physically or via catalog optimization depending on the RDBMS version). New rows inserted without specifying enrollment_date will also receive '2025-01-01'. If you additionally want to prevent NULL values, append NOT NULL: ALTER TABLE students ADD COLUMN enrollment_date DATE NOT NULL DEFAULT '2025-01-01';

Question 3: INTERMEDIATE

You have a table orders(id SERIAL PRIMARY KEY, customer_id INT, total DECIMAL(10,2), status VARCHAR(20)). Write a sequence of ALTER TABLE statements that: (a) add a CHECK constraint ensuring total > 0, (b) add a foreign key from customer_id to customers(id), and (c) change the status column from VARCHAR(20) to VARCHAR(50). Consider which operations require a table scan and which are metadata-only.
Show answer
The three statements are: (a) ALTER TABLE orders ADD CONSTRAINT chk_positive_total CHECK (total > 0); — This requires a full table scan to verify all existing rows satisfy total > 0. (b) ALTER TABLE orders ADD CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES customers(id); — This requires scanning all customer_id values and verifying each exists in the customers table. (c) ALTER TABLE orders ALTER COLUMN status SET DATA TYPE VARCHAR(50); — In PostgreSQL, widening a VARCHAR is metadata-only (only the typmod changes in the catalog). In MySQL, this may require an INPLACE or COPY algorithm depending on the version. The key insight is that constraint additions always validate existing data (O(n) scan), while column type widening behavior is vendor-dependent.

Question 4: APPLIED

You are the DBA for a SaaS platform with a users table containing 50 million rows on PostgreSQL 15. The product team wants to add a NOT NULL timezone VARCHAR(50) DEFAULT 'UTC' column. The service must remain available during the migration with no more than 100ms of lock time. Describe the migration strategy you would use, including specific ALTER TABLE syntax, and explain why it meets the availability requirement.
Show answer
On PostgreSQL 15, adding a column with NOT NULL and a non-volatile DEFAULT is a metadata-only operation (an optimization introduced in PostgreSQL 11). The single statement ALTER TABLE users ADD COLUMN timezone VARCHAR(50) NOT NULL DEFAULT 'UTC'; acquires an ACCESS EXCLUSIVE lock only for the brief duration needed to update the system catalog—typically sub-millisecond even on a 50-million-row table. No table rewrite occurs; the default is stored in pg_attribute and dynamically substituted at read time for rows that predate the change. This comfortably meets the 100ms lock-time budget. If you were on PostgreSQL 10 or earlier, you would instead need a multi-step strategy: (1) ADD COLUMN timezone VARCHAR(50) DEFAULT 'UTC' (nullable, triggering a rewrite), or (2) add nullable first, backfill in batches using UPDATE with LIMIT/OFFSET, then SET NOT NULL—but the SET NOT NULL itself would require a full scan. The key takeaway is that version awareness directly determines your migration strategy.

Question 5: CRITICAL THINKING

A junior developer proposes the following migration for a high-traffic MySQL 8.0 table with 200 million rows: ALTER TABLE transactions MODIFY COLUMN amount DECIMAL(12,2), ADD INDEX idx_amount (amount), ADD CONSTRAINT fk_account FOREIGN KEY (account_id) REFERENCES accounts(id); Critically analyze this migration. Identify at least three potential problems and propose a safer alternative migration plan.
Show answer
This migration has several serious issues. First, combining three operations in a single ALTER TABLE statement means MySQL must evaluate the most restrictive algorithm—likely COPY—for the entire operation, holding an exclusive lock for the duration of a 200M-row rewrite. If any sub-operation fails, all three are rolled back (and since MySQL uses implicit commits for DDL, the rollback itself is another full rewrite). Second, MODIFY COLUMN from one DECIMAL precision to another may require a table rebuild where every row's amount field is physically rewritten. Third, the foreign key addition scans all 200M rows to validate account_id references, compounding the rewrite time. A safer plan: (1) ALTER TABLE transactions MODIFY COLUMN amount DECIMAL(12,2), ALGORITHM=INPLACE, LOCK=NONE; — verify this qualifies for INPLACE by checking the MySQL documentation for the specific type change. (2) CREATE INDEX idx_amount ON transactions(amount) — in MySQL 8.0 this can use ALGORITHM=INPLACE, LOCK=NONE. (3) For the FK, consider adding it with an application-level check first, then ALTER TABLE transactions ADD CONSTRAINT fk_account FOREIGN KEY (account_id) REFERENCES accounts(id); as a separate step during low-traffic hours—or use pt-online-schema-change to avoid holding a lock on the original table. Separating operations gives you granular control over locking behavior and failure isolation.

SUMMARY

ALTER TABLE — Summary

Summary

ALTER TABLE is the primary DDL statement for schema evolution in relational databases, enabling non-destructive modifications to existing tables. Its core operations include ADD COLUMN, DROP COLUMN, ALTER COLUMN (for type and nullability changes), ADD/DROP CONSTRAINT (for PRIMARY KEY, FOREIGN KEY, UNIQUE, CHECK, and NOT NULL enforcement), and RENAME operations for both tables and columns. Understanding the distinction between metadata-only operations (which execute in constant time) and table-rewrite operations (which scale linearly with row count) is critical for production database management.

Behavior varies significantly across RDBMS platforms: PostgreSQL supports transactional DDL and metadata-only defaults (v11+), while MySQL uses implicit commits and requires careful algorithm selection (INSTANT, INPLACE, COPY). In production, ALTER TABLE statements are orchestrated through migration frameworks (Flyway, Liquibase, Alembic) and advanced patterns like expand-contract migrations and ghost table migrations to achieve zero-downtime schema changes at scale.

Varsity Tutors • SQL • ALTER TABLE