Semua tabel yang kita rancang sepanjang section ini dibuat sekali jadi — skenario ideal yang jarang bertahan lama. Kebutuhan bisnis berubah: laporan baru minta kolom ringkasan, kebijakan mutakhir menjadikan kolom opsional jadi wajib, dan kesalahan penamaan baru terlihat setelah aplikasi berjalan. ALTER TABLE adalah perintah untuk memperbaiki tabel yang sudah hidup tanpa membuang datanya. Pada chapter ini kita akan menambah, mengubah tipe, mengganti nama, dan menghapus kolom; me-rename tabel; memasang constraint setelah tabel berisi data; lalu mempelajari dua galat khas saat perubahan bentrok dengan data yang sudah tersimpan — beserta urutan langkah yang membuat migrasi skema tetap aman.

Menyiapkan Tabel Latihan #

Kita pakai tabel artikel sederhana yang sudah berisi dua baris:

sql
CREATE DATABASE belajar_ubah;
USE belajar_ubah;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)

Database changed
sql
CREATE TABLE artikel (
  id INT PRIMARY KEY,
  judul VARCHAR(100) NOT NULL,
  penulis VARCHAR(100)
);

INSERT INTO artikel (id, judul, penulis) VALUES
  (1, 'Belajar Desain Tabel', 'Budi'),
  (2, 'Tips Memilih Tipe Data', 'Ani');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

Menambah Kolom dengan ADD COLUMN #

Perubahan paling ringan adalah menambah kolom baru. Kolom baru tanpa NOT NULL dan tanpa default membuat baris lama terisi NULL — aman karena tidak ada data yang dipaksa berubah:

sql
ALTER TABLE artikel ADD COLUMN ringkasan VARCHAR(255);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0
sql
DESC artikel;

Output yang Dihasilkan:

text
+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| id        | int          | NO   | PRI | NULL    |       |
| judul     | varchar(100) | NO   |     | NULL    |       |
| penulis   | varchar(100) | YES  |     | NULL    |       |
| ringkasan | varchar(255) | YES  |     | NULL    |       |
+-----------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

Kolom ringkasan kini hadir di posisi terakhir dengan nilai NULL pada kedua baris lama. Satu statement ALTER TABLE hanya perlu satu klausul perubahan; beberapa perubahan bisa digabung dengan koma, tetapi memisahkannya membuat tiap perubahan mudah diuji dan dibatalkan.

Mengubah Tipe dengan MODIFY COLUMN #

Klausa MODIFY COLUMN mengubah definisi kolom tanpa mengganti namanya. Memperlebar VARCHAR adalah arah yang aman — tidak ada data yang bisa terpotong:

sql
ALTER TABLE artikel MODIFY COLUMN penulis VARCHAR(150);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

Kapasitas penulis naik dari 100 menjadi 150 karakter. Perubahan yang lebih berbahaya adalah menambah NOT NULL pada kolom yang masih menyimpan NULL. Kita tambahkan dulu satu baris yang penulisnya kosong:

sql
INSERT INTO artikel (id, judul, penulis, ringkasan) VALUES
  (3, 'Jebakan Kolom Opsional', NULL, NULL);

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)
sql
-- @error
ALTER TABLE artikel MODIFY COLUMN penulis VARCHAR(150) NOT NULL;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1138 (22004): Invalid use of NULL value
Bye

Server menolak perubahan itu dengan galat ERROR 1138 (22004): Invalid use of NULL value — baris id 3 masih memuat NULL pada kolom yang ingin dijadikan wajib. Jalan keluarnya: isi dulu datanya, baru ubah strukturnya:

sql
UPDATE artikel SET penulis = 'Tim Redaksi' WHERE id = 3;

ALTER TABLE artikel MODIFY COLUMN penulis VARCHAR(150) NOT NULL;

DESC artikel;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| id        | int          | NO   | PRI | NULL    |       |
| judul     | varchar(100) | NO   |     | NULL    |       |
| penulis   | varchar(150) | NO   |     | NULL    |       |
| ringkasan | varchar(255) | YES  |     | NULL    |       |
+-----------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

Setelah UPDATE mengisi penulis baris ketiga, MODIFY ke NOT NULL berhasil, dan DESC menunjukkan penulis kini varchar(150) dengan Null = NO. Perhatikan pula baris Rows matched: 1 Changed: 1 Warnings: 0 pada output UPDATE — satu baris cocok dengan kondisi WHERE dan satu baris benar-benar berubah nilainya. Inilah miniatur urutan aman migrasi skema: siapkan data dulu, ketatkan struktur belakangan.

Mengganti Nama dengan CHANGE COLUMN dan RENAME TO #

Klausa CHANGE COLUMN mengganti nama sekaligus definisi kolom — nama baru ditulis lebih dulu, definisinya menyusul:

sql
ALTER TABLE artikel CHANGE COLUMN ringkasan sinopsis VARCHAR(255);

DESC artikel;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id       | int          | NO   | PRI | NULL    |       |
| judul    | varchar(100) | NO   |     | NULL    |       |
| penulis  | varchar(150) | NO   |     | NULL    |       |
| sinopsis | varchar(255) | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

Kolom ringkasan berganti nama menjadi sinopsis dengan definisi yang sama. Nama tabel sendiri diganti lewat RENAME TO:

sql
ALTER TABLE artikel RENAME TO tulisan;

SHOW TABLES;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

+------------------------+
| Tables_in_belajar_ubah |
+------------------------+
| tulisan                |
+------------------------+
1 row in set (0.00 sec)

Daftar tabel kini memuat tulisan. Perintah RENAME tidak menyentuh isi data maupun kolom — hanya identitas tabelnya yang berubah, sehingga seluruh query lama yang menyebut artikel harus diperbarui setelahnya.

Menghapus Kolom dengan DROP COLUMN #

Menghapus kolom bersifat permanen: datanya ikut lenyap dan tidak bisa dibatalkan dengan ALTER lain.

sql
ALTER TABLE tulisan DROP COLUMN sinopsis;

DESC tulisan;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int          | NO   | PRI | NULL    |       |
| judul   | varchar(100) | NO   |     | NULL    |       |
| penulis | varchar(150) | NO   |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

Struktur tulisan kembali ramping. Sebelum menghapus kolom produksi, pastikan tidak ada view, stored procedure, atau query aplikasi yang masih membacanya.

Memasang Constraint Setelah Tabel Berisi Data #

Constraint tidak harus hadir sejak CREATE TABLE. Kita tambahkan kolom slug dulu, lalu menegakkannya dengan ADD UNIQUE:

sql
ALTER TABLE tulisan ADD COLUMN slug VARCHAR(100);

ALTER TABLE tulisan ADD UNIQUE slug_unik (slug);

DESC tulisan;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id      | int          | NO   | PRI | NULL    |       |
| judul   | varchar(100) | NO   |     | NULL    |       |
| penulis | varchar(150) | NO   |     | NULL    |       |
| slug    | varchar(100) | YES  | UNI | NULL    |       |
+---------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

Kolom slug tampil dengan penanda UNI di kolom Key — constraint unik bernama slug_unik kini aktif meski dipasang belakangan. Penegakannya langsung terasa:

sql
INSERT INTO tulisan (id, judul, penulis, slug) VALUES
  (4, 'Kunci yang Tertukar', 'Siti', 'kunci-tertukar');

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)
sql
-- @error
INSERT INTO tulisan (id, judul, penulis, slug) VALUES (5, 'Kunci Kembar', 'Budi', 'kunci-tertukar');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1062 (23000): Duplicate entry 'kunci-tertukar' for key 'tulisan.slug_unik'
Bye

Galat 1062 menolak slug kembar — constraint baru bekerja persis seperti yang dipasang sejak awal. Pola yang sama berlaku untuk primary key dan foreign key. Dua contoh berikut memasangnya pada tabel yang baru dibuat tanpa kunci:

sql
CREATE TABLE penanda (kode INT NOT NULL, label VARCHAR(50));

ALTER TABLE penanda ADD PRIMARY KEY (kode);

DESC penanda;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| kode  | int         | NO   | PRI | NULL    |       |
| label | varchar(50) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
sql
CREATE TABLE penulis (
  id INT PRIMARY KEY,
  nama VARCHAR(100) NOT NULL
);

CREATE TABLE buku (
  id INT PRIMARY KEY,
  penulis_id INT,
  judul VARCHAR(100) NOT NULL
);

ALTER TABLE buku ADD CONSTRAINT fk_buku_penulis
  FOREIGN KEY (penulis_id) REFERENCES penulis (id);

INSERT INTO penulis (id, nama) VALUES (1, 'Budi');

INSERT INTO buku (id, penulis_id, judul) VALUES (1, 1, 'Belajar Desain Tabel');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Syarat penambahan constraint belakangan tetap sama seperti saat pembuatan: data yang ada harus sudah mematuhi aturannya — ADD PRIMARY KEY gagal bila ada duplikat, dan ADD FOREIGN KEY gagal bila ada baris yatim.

Menyusutkan Kolom yang Berisi Data Panjang #

Arah berbahaya dari MODIFY adalah menyusutkan kapasitas. Kita buktikan pada tabel kecil:

sql
CREATE TABLE pendek (
  id INT PRIMARY KEY,
  nama VARCHAR(50)
);

INSERT INTO pendek VALUES (1, 'Nama Kolom Yang Sangat Panjang Sekali');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)
sql
-- @error
ALTER TABLE pendek MODIFY COLUMN nama VARCHAR(10);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1265 (01000): Data truncated for column 'nama' at row 1
Bye

Perubahan itu ditolak dengan galat ERROR 1265 (01000): Data truncated for column 'nama' at row 1 — memangkas kolom menjadi 10 karakter berarti memotong nilai sepanjang 37 karakter, dan mode ketat melarang pemotongan senyap. Bila penyusutan memang harus dilakukan, jalankan UPDATE yang memangkas nilainya terlebih dahulu, baru MODIFY kolomnya.

Urutan Aman Migrasi Skema #

Dua galat di atas (1138 dan 1265) mengajarkan pola yang sama: struktur baru hanya boleh ditegakkan setelah data siap. Urutan aman menambah kolom wajib pada tabel produksi:

  1. Tambah kolom sebagai opsional — ADD COLUMN tanpa NOT NULL, sehingga baris lama terisi NULL dan aplikasi lama tetap berjalan.
  2. Perbarui data yang kosong — isi baris lama lewat UPDATE sampai tidak ada NULL yang tersisa.
  3. Ketatkan strukturnya — MODIFY COLUMN ... NOT NULL setelah semua baris terisi.
  4. Perbarui aplikasi — pastikan seluruh INSERT baru sudah mengirim kolom tersebut sebelum langkah 3, agar pengguna tidak menabrak galat 1364.

Referensi #

Tabel kini bisa tumbuh mengikuti kebutuhan: kolom ditambah dan diketatkan, nama diperbaiki, constraint dipasang belakangan — semuanya tanpa kehilangan data yang sudah terkumpul. Dengan ini pula perangkat desain tabel kita lengkap: dari merancang kolom pertama sampai memigrasikan struktur yang sudah dipakai. Section berikutnya melangkah ke lapisan data itu sendiri — cara menyisipkan, membaca, mengubah, dan menghapus isi tabel dengan DML. Sebelum itu, kita bereskan database latihan:

sql
DROP DATABASE IF EXISTS belajar_ubah;

Output yang Dihasilkan:

text
Query OK, 5 rows affected (0.00 sec)

Lima tabel — tulisan, pendek, penanda, penulis, dan buku — ikut terbuang.