Baris tanpa identitas adalah baris yang tidak bisa dipertanggungjawabkan: dua entri identik masuk, tidak ada cara membedakannya, dan perintah UPDATE atau DELETE bisa salah sasaran karena keduanya tak bisa dibedakan. Primary key adalah mekanisme MySQL untuk menutup celah tersebut — satu kolom (atau gabungan kolom) yang menjamin setiap baris punya identitas unik. Pada chapter ini kita akan memasang primary key satu kolom, menyaksikan server menolak nilai duplikat maupun NULL, membandingkan kunci sintetis dengan natural key, lalu melihat apa yang terjadi pada tabel yang mencoba hidup tanpa primary key.

Apa itu Primary Key #

Primary key (PK) adalah constraint yang mengikat satu kolom sebagai identitas baris. Klausa PRIMARY KEY pada definisi kolom memberikan tiga jaminan sekaligus:

  • Unik: tidak ada dua baris dengan nilai PK yang sama.
  • Tidak boleh NULL: baris tanpa identitas ditolak sejak awal.
  • Terindeks: server membangun index khusus untuk mencari baris lewat PK dengan cepat.

Ketiga jaminan tersebut dijaga pada saat INSERT dan UPDATE — server memeriksa setiap nilai baru sebelum baris tersimpan, sehingga pelanggaran langsung memicu galat, bukan menjadi data kotor yang ditemukan berbulan-bulan kemudian.

Memasang PK Satu Kolom #

Kita siapkan database latihan dan tabel kategori untuk toko alat tulis. Kolom id berperan sebagai kunci sintetis: angka teknis yang tidak bermakna di dunia nyata, tetapi bebas dari perubahan data:

sql
CREATE DATABASE belajar_pk;
USE belajar_pk;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)

Database changed
sql
CREATE TABLE kategori (
  id INT PRIMARY KEY,
  nama VARCHAR(50) NOT NULL
);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Mari kita bedah dua baris definisi tersebut:

  1. Baris 2 (id INT PRIMARY KEY): Kolom id bertipe INT; klausa PRIMARY KEY langsung menempel pada definisi kolom. Satu statement ini sekaligus menjadikan id unik, wajib terisi, dan terindeks.
  2. Baris 3 (nama VARCHAR(50) NOT NULL): Nama kategori wajib terisi agar tabel tidak memuat kategori tanpa nama.

Kita isi tabel dengan tiga kategori:

sql
INSERT INTO kategori (id, nama) VALUES
  (1, 'Alat Tulis'),
  (2, 'Buku'),
  (3, 'Kertas');

SELECT id, nama FROM kategori ORDER BY id;

Output yang Dihasilkan:

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

+----+------------+
| id | nama       |
+----+------------+
|  1 | Alat Tulis |
|  2 | Buku       |
|  3 | Kertas     |
+----+------------+
3 rows in set (0.00 sec)

INSERT Duplikat Ditolak #

Primary key mulai menunjukkan giginya begitu data kedua kalinya mencoba memakai identitas yang sudah terpakai. Statement berikut sengaja mengulang id = 2:

sql
-- @error
INSERT INTO kategori (id, nama) VALUES (2, 'Pulpen');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1062 (23000): Duplicate entry '2' for key 'kategori.PRIMARY'
Bye

Server menolak baris tersebut dengan galat ERROR 1062 (23000): Duplicate entry '2' for key 'kategori.PRIMARY' — nama kunci kategori.PRIMARY menunjuk primary key tabel kategori sebagai penjaga keunikan. Karena statement gagal seluruhnya, tabel tetap berisi tiga baris semula; tidak ada baris baru yang setengah tersimpan. Angka 2 pada pesan adalah nilai duplikat yang mencoba masuk, sehingga kita langsung tahu baris mana yang bermasalah.

PK Menolak NULL #

Identitas yang tidak diketahui bukan identitas. Kolom PK secara implisit menjadi NOT NULL, sehingga statement berikut gagal walau kita tidak pernah menulis NOT NULL pada kolom id:

sql
-- @error
INSERT INTO kategori (id, nama) VALUES (NULL, 'Spidol');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1048 (23000): Column 'id' cannot be null
Bye

Galat ERROR 1048 (23000): Column 'id' cannot be null menegaskan aturan tersebut. Kombinasi dua uji di atas penting dipahami bersama: primary key menolak nilai ganda dengan 1062 dan menolak nilai kosong dengan 1048. Struktur kolomnya bisa kita pastikan lewat DESC:

sql
DESC kategori;

Output yang Dihasilkan:

text
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| nama  | varchar(50) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

Perhatikan kolom Key bernilai PRI pada id — penanda primary key — dan kolom Null bernilai NO meskipun klausa NOT NULL tidak pernah kita tulis untuk id. Sifat wajib-terisi tersebut datang otomatis dari statusnya sebagai primary key.

Kunci Sintetis vs Natural Key #

Kolom id tadi adalah kunci sintetis (surrogate key): angka yang tidak punya arti di luar database. Alternatifnya adalah natural key — kolom yang memang unik di dunia nyata, seperti alamat email atau nomor polisi. MySQL menerima keduanya; tabel berikut memakai email sebagai primary key:

sql
CREATE TABLE member_toko (
  email VARCHAR(100) PRIMARY KEY,
  nama VARCHAR(100) NOT NULL
);

INSERT INTO member_toko (email, nama) VALUES
  ('[email protected]', 'Budi'),
  ('[email protected]', 'Siti');

SELECT email, nama FROM member_toko ORDER BY email;

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

+------------------+------+
| email            | nama |
+------------------+------+
| [email protected] | Budi |
| [email protected] | Siti |
+------------------+------+
2 rows in set (0.00 sec)

email duplikat akan ditolak dengan galat 1062 yang sama seperti pada tabel kategori. Lalu kapan kita memilih sintetis dan kapan natural? Panduan praktisnya:

AspekKunci sintetis (id)Natural key (email)
Stabilitastidak pernah berubahbisa berubah (ganti email)
Ukuran indexkecil dan seragam (INT)lebih besar (VARCHAR)
Keterbacaanbutuh join untuk tahu maknanyalangsung bermakna
Risiko data nyatarendah — murni teknisbergantung keunikan dunia nyata

Kunci natural yang bisa berubah adalah risiko besar: nilai primary key yang berubah memaksa seluruh foreign key yang mengarah padanya ikut diperbarui. Karena itu banyak tim memilih kunci sintetis INT AUTO_INCREMENT sebagai default aman — kombinasi yang akan kita bahas tuntas pada chapter AUTO INCREMENT.

Tabel Tanpa Primary Key #

MySQL tidak melarang tabel tanpa PK — statement CREATE TABLE tetap berhasil. Namun database produksi sering mengaktifkan pengawas sql_require_primary_key supaya tabel tanpa PK ditolak sejak lahir, karena tabel tanpa identitas menyulitkan replikasi dan pencarian cepat. Kita simulasikan pengawas tersebut pada level sesi:

sql
-- @error
SET SESSION sql_require_primary_key = 1;
CREATE TABLE log_aktivitas (pesan VARCHAR(200));

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

ERROR 3750 (HY000): Unable to create or change a table without a primary key, when the system variable 'sql_require_primary_key' is set. Add a primary key to the table or unset this variable to avoid this message. Note that tables without a primary key can cause performance problems in row-based replication, so please consult your DBA before changing this setting.
Bye

Galat ERROR 3750 menolak pembuatan tabel log_aktivitas karena struktur tanpa primary key, dan pesannya menyarankan dua jalan keluar: pasang primary key atau matikan variabel pengawasnya. Pengaturan SET SESSION hanya berlaku pada sesi kita sendiri, sehingga tidak mengganggu sesi lain.

Setiap baris tabel kategori dan member_toko kini punya identitas yang dijaga server, dan identitas itu baru bermakna penuh ketika tabel lain dapat mengarah ke baris tertentu secara resmi — tugas yang diambil alih oleh FOREIGN KEY pada chapter berikutnya. Sebelum pindah topik, kita bereskan database latihan:

sql
DROP DATABASE IF EXISTS belajar_pk;

Output yang Dihasilkan:

text
Query OK, 2 rows affected (0.00 sec)

Angka 2 rows affected mencerminkan dua tabel — kategori dan member_toko — yang ikut dibuang bersama database-nya.

Referensi #