Program yang kita tulis sejauh ini menyimpan data di memori: list, dictionary, dan object semuanya hilang begitu program berhenti. Aplikasi nyata seperti kasir toko tidak boleh kehilangan daftar produk setiap kali komputer dimatikan. Database relasional menyelesaikan masalah ini dengan menyimpan data secara permanen di disk, dalam struktur yang bisa dicari, divalidasi, dan dihubungkan antar satu dengan yang lain.
Tabel, Baris, dan Kolom #
Database relasional menyimpan data dalam tabel: struktur seperti spreadsheet yang punya nama. Setiap kolom menyimpan satu jenis informasi dengan tipe data tetap, dan setiap baris adalah satu entri data. Untuk domain inventaris toko, tabel produk kita berisi empat kolom:
| Kolom | Isi | Tipe umum |
|---|---|---|
id | identitas unik tiap produk | INTEGER |
nama | nama produk | TEXT |
harga | harga satuan dalam rupiah | REAL |
stok | jumlah unit di gudang | INTEGER |
Satu baris pada tabel tersebut misalnya (1, 'Kopi Hitam 200g', 18000, 12). Berbeda dengan spreadsheet, database menolak baris yang melanggar aturan: kolom bertipe angka tidak bisa diisi teks, dan kolom yang ditandai NOT NULL tidak boleh dibiarkan kosong.
Primary Key dan Foreign Key #
Primary key adalah kolom (atau gabungan kolom) yang menjadi identitas unik setiap baris. Kolom id pada tabel produk adalah primary key klasik: dua produk boleh bernama sama, tetapi tidak boleh berbagi id. Mesin database aktif menjaga aturan ini, bukan sekadar konvensi. Mari kita buktikan dengan SQLite bawaan Python:
import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute("""CREATE TABLE produk (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
harga REAL NOT NULL,
stok INTEGER NOT NULL DEFAULT 0
)""")
conn.execute("INSERT INTO produk VALUES (1, 'Gula Pasir 1kg', 15500, 25)")
conn.commit()
try:
conn.execute("INSERT INTO produk VALUES (1, 'Kopi Hitam 200g', 18000, 12)")
except sqlite3.IntegrityError as e:
print("Galat:", e)
print("Jumlah produk:", conn.execute("SELECT COUNT(*) FROM produk").fetchone()[0])
conn.close()Output yang Dihasilkan:
Galat: UNIQUE constraint failed: produk.id
Jumlah produk: 1Mari kita telaah baris-baris pentingnya:
- Baris 3 (
conn = sqlite3.connect(":memory:")): membuat koneksi ke database sementara di memori. Pola lengkapnya kita bahas tuntas pada Chapter SQLite dengan Python. - Baris 4-9 (
CREATE TABLE ...): mendefinisikan tabelprodukbeserta tipe tiap kolom;INTEGER PRIMARY KEYpadaidmemicu pemeriksaan keunikan otomatis. - Baris 10 (
INSERT ... VALUES (1, ...)): menyisipkan baris pertama denganid = 1. - Baris 13 (
conn.execute("INSERT INTO produk VALUES (1, ...")): mencoba menyisipkan baris kedua denganidyang sama; mesin SQLite menolaknya dengansqlite3.IntegrityError. - Baris 17 (
print("Jumlah produk:", ...)): membuktikan tabel tetap berisi satu baris — percobaan duplikat tidak pernah tersimpan.
Foreign key adalah kolom yang merujuk primary key di tabel lain. Tabel penjualan menyimpan produk_id yang harus cocok dengan salah satu id di tabel produk, sehingga tidak mungkin ada transaksi untuk produk yang tidak pernah ada:
import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute("PRAGMA foreign_keys = ON")
conn.execute("CREATE TABLE produk (id INTEGER PRIMARY KEY, nama TEXT NOT NULL)")
conn.execute("""CREATE TABLE penjualan (
id INTEGER PRIMARY KEY,
produk_id INTEGER NOT NULL REFERENCES produk(id),
jumlah INTEGER NOT NULL
)""")
conn.execute("INSERT INTO produk VALUES (1, 'Gula Pasir 1kg')")
conn.commit()
try:
conn.execute("INSERT INTO penjualan VALUES (1, 99, 3)")
except sqlite3.IntegrityError as e:
print("Galat:", e)
print("Jumlah penjualan:", conn.execute("SELECT COUNT(*) FROM penjualan").fetchone()[0])
conn.close()Output yang Dihasilkan:
Galat: FOREIGN KEY constraint failed
Jumlah penjualan: 0Penjualan dengan produk_id = 99 ditolak karena tabel produk tidak memiliki baris dengan id = 99.
Tiga Bentuk Relasi #
Cara tabel saling terhubung disebut relasi, dan ada tiga bentuk dasarnya:
- Satu-ke-satu (1-1): satu baris tabel A berpasangan dengan tepat satu baris tabel B. Contoh: satu toko memiliki satu profil pajak. Bentuk ini jarang dipakai karena biasanya lebih sederhana menggabungkannya ke satu tabel.
- Satu-ke-banyak (1-N): satu penjualan memuat banyak baris detail penjualan. Ini bentuk relasi paling umum di dunia nyata.
- Banyak-ke-banyak (N-N): satu produk bisa masuk banyak kategori, dan satu kategori menampung banyak produk. Bentuk ini membutuhkan junction table (
produk_kategori) yang berisi pasanganproduk_iddankategori_id.
Skema inventaris toko kita hingga chapter ini tergambar sebagai berikut:
Notasi ||--o{ berarti “tepat satu” ke “nol atau banyak”: satu penjualan memuat satu atau banyak detail, sementara satu produk boleh saja belum pernah tercatat di penjualan mana pun.
ACID dan Peta RDBMS #
Database relasional menjanjikan ACID — empat jaminan yang membuat data bisa dipercaya:
| Sifat | Jaminan |
|---|---|
| Atomicity | rangkaian perubahan terjadi seluruhnya atau tidak sama sekali |
| Consistency | aturan tabel (tipe, primary key, foreign key) tidak pernah dilanggar |
| Isolation | transaksi yang berjalan bersamaan tidak saling merusak |
| Durability | perubahan yang sudah disimpan bertahan meski aplikasi mati |
Kita akan merasakan langsung jaminan atomicity pada Chapter Transactions. Terakhir, kenali medannya — SQL yang kita pelajari di sini berlaku di semuanya:
| RDBMS | Bentuk | Kegunaan umum |
|---|---|---|
| SQLite | satu file, tanpa server | aplikasi desktop dan mobile, prototipe, analisis data |
| PostgreSQL | server, open source | aplikasi web serius, data spasial, pencarian teks |
| MySQL / MariaDB | server, open source | aplikasi web, CMS seperti WordPress |
| SQL Server | server, komersial (Microsoft) | ekosistem enterprise Windows dan .NET |
| Oracle Database | server, komersial | sistem perbankan dan enterprise berskala besar |
Dengan konsep tabel, primary key, foreign key, dan relasi di tangan, kita siap menulis SQL pertama dari Python pada Chapter SQL Dasar untuk Python.