Foreign Key

Menghubungkan tabel dengan REFERENCES dan mengaktifkan penegakannya melalui pragma foreign_keys.

Daftar isi

Foreign key adalah constraint yang menghubungkan kolom pada satu tabel dengan kolom pada tabel lain, biasanya dari kolom tabel anak menuju pengenal tabel induk. Hubungan ini menjaga konsistensi referensi antartabel, misalnya setiap peminjaman wajib menunjuk buku yang benar-benar ada. Topik ini membahas klausa REFERENCES, pengaktifan penegakan melalui PRAGMA foreign_keys, pembacaan peta referensi dengan foreign_key_list, serta aksi ON DELETE.

Merujuk tabel lain dengan REFERENCES#

Dalam hubungan referensi, tabel yang memuat pengenal asli disebut tabel induk, sedangkan tabel yang merujuknya disebut tabel anak. Klausa REFERENCES ditempatkan pada definisi kolom anak, diikuti nama tabel induk dan kolom yang dirujuk dalam tanda kurung. Kolom yang dirujuk wajib berupa PRIMARY KEY atau kolom berconstraint UNIQUE, sebab SQLite harus dapat menemukan satu baris induk yang tepat untuk setiap nilai rujukan.

Tabel peminjaman pada skema perpustakaan menjadi contoh yang tepat. Setiap barisnya mewakili satu kejadian peminjaman, dan kolom buku_id menyimpan pengenal buku yang dipinjam.

sql Contoh: kolom yang merujuk tabel lain
CREATE TABLE peminjaman (
    id      INTEGER PRIMARY KEY,
    buku_id INTEGER REFERENCES buku(id),
    tanggal TEXT NOT NULL
);

Deklarasi di atas menyatakan bahwa nilai buku_id seharusnya selalu ada pada kolom buku.id. Satu tabel anak boleh memuat beberapa kolom referensi sekaligus, dan setiap kolomnya menyatakan induk masing-masing, sebagaimana peminjaman yang merujuk buku sekaligus anggota. REFERENCES juga dapat ditempatkan pada tingkat tabel sebagai table constraint apabila referensinya melibatkan kombinasi beberapa kolom. Pada kedua bentuk tersebut, makna deklarasinya sama, yaitu nilai kolom anak wajib dapat ditemukan pada kolom induknya.

Sayangnya, deklarasi saja belum cukup untuk menjamin integritas referensi tersebut. Jika penegakan tidak diaktifkan, data peminjaman yang merujuk pada buku dengan ID 999 tetap dapat disimpan, meskipun buku tersebut tidak pernah ada. Data semacam ini disebut sebagai baris tanpa referensi valid (orphan row). Pada bagian berikutnya, akan dibahas pragma yang digunakan untuk mencegah terjadinya referensi tidak valid tersebut.

Penegakan foreign key dan pragma foreign_keys#

SQLite mendukung penegakan foreign key sejak versi 3.6.19 (2009), tetapi pengendalinya bersifat per koneksi melalui PRAGMA foreign_keys. Banyak build SQLite, termasuk sqlite3 CLI, menetapkan nilai bawaan OFF demi kompatibilitas dengan program lama. Akibatnya, skema yang memuat REFERENCES dapat terlihat benar, padahal tidak ada satu pun pelanggaran yang ditolak.

Blok berikut memperagakan kondisi bawaan tersebut. Query PRAGMA foreign_keys tanpa argumen mengembalikan status penegakan yang sedang berlaku, kemudian satu baris peminjaman dengan buku_id 999 dimasukkan ke tabel yang kosong.

sql Contoh: foreign key tidak ditegakkan secara default
CREATE TABLE buku (
    id    INTEGER PRIMARY KEY,
    judul TEXT NOT NULL
);

CREATE TABLE peminjaman (
    id      INTEGER PRIMARY KEY,
    buku_id INTEGER REFERENCES buku(id),
    tanggal TEXT NOT NULL
);

PRAGMA foreign_keys;

INSERT INTO peminjaman (buku_id, tanggal) VALUES (999, '2026-05-01');

SELECT id, buku_id, tanggal FROM peminjaman;
text Keluaran
0
1|999|2026-05-01

Nilai 0 pada baris pertama keluaran menunjukkan bahwa penegakan batasan kunci asing sedang dinonaktifkan. Akibatnya, baris peminjaman dengan buku_id 999 tetap dapat disimpan meskipun buku yang dirujuk tidak ada, tanpa menghasilkan peringatan. Blok berikutnya mengulangi skenario yang sama dengan mengaktifkan penegakan kunci asing terlebih dahulu.

sql Contoh: penegakan dengan PRAGMA foreign_keys = ON
CREATE TABLE buku (
    id    INTEGER PRIMARY KEY,
    judul TEXT NOT NULL
);

CREATE TABLE peminjaman (
    id      INTEGER PRIMARY KEY,
    buku_id INTEGER REFERENCES buku(id),
    tanggal TEXT NOT NULL
);

PRAGMA foreign_keys = ON;

INSERT INTO buku (judul) VALUES ('Peta Langit');

INSERT INTO peminjaman (buku_id, tanggal) VALUES (1, '2026-05-01');

INSERT INTO peminjaman (buku_id, tanggal) VALUES (999, '2026-05-02');

SELECT id, buku_id, tanggal FROM peminjaman;
text Keluaran
Error near line 18: FOREIGN KEY constraint failed
1|1|2026-05-01

Sekarang peminjaman untuk buku 999 ditolak dengan pesan error yang jelas, sedangkan peminjaman untuk buku 1 yang benar-benar ada tetap tersimpan. Perlu dicatat bahwa status pragma melekat pada koneksi, bukan pada berkas basis data, sehingga koneksi baru selalu kembali ke nilai bawaannya. Pragma ini juga diabaikan apabila dijalankan di dalam transaksi, sehingga pengaktifannya harus dilakukan sebelum BEGIN.

Membaca peta referensi dengan foreign_key_list#

Skema yang berkembang cepat dapat memiliki banyak referensi sekaligus, dan PRAGMA foreign_key_list menampilkan seluruh foreign key milik satu tabel. Blok bash berikut membangun ulang basis data perpustakaan yang lebih lengkap, yaitu tabel buku, anggota, dan peminjaman yang merujuk keduanya dengan aksi penghapusan yang berbeda.

bash
# bangun basis data perpustakaan dengan referensi antartabel
rm -f perpustakaan.db
sqlite3 perpustakaan.db <<'SQL'
CREATE TABLE buku (
    id    INTEGER PRIMARY KEY,
    judul TEXT NOT NULL
);
CREATE TABLE anggota (
    id   INTEGER PRIMARY KEY,
    nama TEXT NOT NULL
);
CREATE TABLE peminjaman (
    id         INTEGER PRIMARY KEY,
    buku_id    INTEGER REFERENCES buku(id) ON DELETE CASCADE,
    anggota_id INTEGER REFERENCES anggota(id) ON DELETE SET NULL,
    tanggal    TEXT NOT NULL
);
INSERT INTO buku (id, judul) VALUES
    (1, 'Dasar Algoritma'),
    (2, 'Peta Langit'),
    (3, 'Kisah Kota Lama');
INSERT INTO anggota (id, nama) VALUES
    (1, 'Ali'),
    (2, 'Budi');
INSERT INTO peminjaman (buku_id, anggota_id, tanggal) VALUES
    (1, 1, '2026-05-01'),
    (2, 1, '2026-05-03'),
    (3, 2, '2026-05-05');
SQL

Perintah berikut membaca peta referensi tabel peminjaman dari basis data yang baru dibangun.

bash
# tampilkan peta foreign key pada tabel peminjaman
sqlite3 perpustakaan.db "PRAGMA foreign_key_list(peminjaman);"
text Keluaran
0|0|anggota|anggota_id|id|NO ACTION|SET NULL|NONE
1|0|buku|buku_id|id|NO ACTION|CASCADE|NONE

Setiap baris keluaran memuat delapan kolom, yaitu nomor urut internal foreign key, indeks kolom pada constraint multi kolom, nama tabel induk, kolom anak, kolom induk yang dirujuk, aksi ON UPDATE, aksi ON DELETE, dan mode pencocokan. Baris pertama menunjukkan anggota_id yang merujuk anggota.id dengan aksi hapus SET NULL, sedangkan baris kedua menunjukkan buku_id yang merujuk buku.id dengan aksi CASCADE. Urutan baris mengikuti struktur internal, sehingga tidak perlu dianggap sebagai urutan deklarasi.

Query ini tidak menuntut penegakan aktif, sehingga dapat dijalankan kapan pun untuk mengaudit skema. Perlu diperhatikan bahwa keluarannya hanya memuat foreign key yang dideklarasikan lewat REFERENCES, bukan hubungan antartabel yang hanya tersirat pada nama kolom. Audit semacam ini berguna sebelum menjalankan prosedur perubahan schema, karena prosedur tersebut menuntut daftar referensi yang lengkap.

Aksi ON DELETE CASCADE dan SET NULL#

Tanpa aksi khusus, aksi hapus pada foreign key adalah NO ACTION, yaitu penghapusan baris induk yang masih dirujuk ditolak ketika penegakan aktif. Aksi ON DELETE CASCADE mengganti penolakan itu dengan penghapusan otomatis seluruh baris anak yang terdampak, sedangkan ON DELETE SET NULL mengosongkan kolom rujukan pada baris anak. Kolom dengan aksi SET NULL tentu tidak boleh dideklarasikan NOT NULL, karena SQLite perlu mengisinya dengan NULL.

Blok berikut menghapus buku 1 dengan penegakan aktif, lalu menampilkan sisa baris peminjaman.

bash
# hapus buku 1 dengan penegakan foreign key aktif
sqlite3 perpustakaan.db <<'SQL'
PRAGMA foreign_keys = ON;
DELETE FROM buku WHERE id = 1;
SELECT id, buku_id, anggota_id, tanggal FROM peminjaman;
SQL
text Keluaran
2|2|1|2026-05-03
3|3|2|2026-05-05

Baris peminjaman pertama yang merujuk buku 1 hilang bersama bukunya akibat CASCADE, dan hanya dua baris yang tersisa. Blok berikut menghapus anggota 1 untuk memperagakan SET NULL, dengan .nullvalue NULL membuat NULL tampil jelas pada keluaran.

bash
# hapus anggota 1, lalu periksa kolom anggota_id
sqlite3 perpustakaan.db <<'SQL'
PRAGMA foreign_keys = ON;
.nullvalue NULL
DELETE FROM anggota WHERE id = 1;
SELECT id, buku_id, anggota_id, tanggal FROM peminjaman;
SQL
text Keluaran
2|2|NULL|2026-05-03
3|3|2|2026-05-05

Setelah anggota Ali dihapus, kolom anggota_id pada peminjaman pertama menjadi NULL sesuai aksi SET NULL, sedangkan peminjaman kedua tetap merujuk Budi yang tidak dihapus. Riwayat peminjaman tidak ikut terhapus, sehingga aksi SET NULL cocok untuk data historis yang masih perlu dibaca. Pilihan antara CASCADE dan SET NULL dengan demikian merupakan keputusan desain tentang makna data, bukan sekadar pilihan teknis.

Ringkasan#

  • REFERENCES menghubungkan kolom anak ke kolom induk, dan kolom induk wajib berupa PRIMARY KEY atau kolom UNIQUE.
  • Penegakan dikendalikan per koneksi dengan PRAGMA foreign_keys, yang bawaannya OFF pada banyak build, sehingga harus diaktifkan di awal setiap sesi.
  • ON DELETE CASCADE menghapus baris anak bersama induknya, sedangkan ON DELETE SET NULL mengosongkan kolom rujukan.
  • PRAGMA foreign_key_list menampilkan peta referensi beserta aksi hapus dan ubah setiap foreign key.

Referensi#