Section Dasar SQL sudah membiasakan kita menulis query terhadap tabel pelanggan, produk, dan pesanan milik toko online. Namun query hanyalah bahasa — di baliknya ada model penyimpanan yang menjelaskan mengapa data berbentuk tabel, baris punya pengenal unik, dan tipe data menentukan apa yang boleh disimpan. Model itu bernama database relasional; memahaminya adalah syarat sebelum kita bisa merancang skema sendiri dan memilih sistem penyimpanan untuk pipeline produksi.

Tabel, Baris, dan Kolom #

Model relasional menyimpan data dalam tabel (relation): kumpulan baris (record) seragam yang masing-masing terdiri atas kolom (attribute) bernama dan bertipe tetap. Satu baris mewakili satu entitas dunia nyata — satu pelanggan, satu produk — dan setiap baris harus bisa dibedakan dari baris lain, minimal lewat kombinasi nilai kolomnya.

Kita buat tabel pelanggan kecil lewat modul sqlite3 Python:

python
import sqlite3

koneksi = sqlite3.connect(":memory:")
koneksi.execute("""
    CREATE TABLE pelanggan (
        id INTEGER PRIMARY KEY,
        nama TEXT NOT NULL,
        kota TEXT,
        saldo INTEGER NOT NULL DEFAULT 0
    )
""")
koneksi.executemany(
    "INSERT INTO pelanggan (id, nama, kota, saldo) VALUES (?, ?, ?, ?)",
    [(1, "Budi Santoso", "Jakarta", 150000),
     (2, "Siti Rahayu", "Bandung", 75000),
     (3, "Dewi Lestari", "Surabaya", 200000)],
)

for baris in koneksi.execute("SELECT * FROM pelanggan"):
    print(baris)

Output yang Dihasilkan:

text
(1, 'Budi Santoso', 'Jakarta', 150000)
(2, 'Siti Rahayu', 'Bandung', 75000)
(3, 'Dewi Lestari', 'Surabaya', 200000)

Setiap baris tampil sebagai tuple Python. Kolom kota sengaja tanpa NOT NULL karena pelanggan baru boleh belum mengisi kota — NULL menandai “nilai belum tersedia”, bukan string kosong. Struktur tabel bisa diperiksa tanpa membaca datanya:

python
for baris in koneksi.execute("PRAGMA table_info(pelanggan)"):
    print(baris)

Output yang Dihasilkan:

text
(0, 'id', 'INTEGER', 0, None, 1)
(1, 'nama', 'TEXT', 1, None, 0)
(2, 'kota', 'TEXT', 0, None, 0)
(3, 'saldo', 'INTEGER', 1, '0', 0)

Kolom keempat bernilai 1 menandai NOT NULL, dan kolom terakhir bernilai 1 hanya pada id — penanda primary key. PRAGMA adalah cara SQLite membuka metadata internalnya; skema DDL aslinya tersimpan dalam tabel sistem sqlite_master.

Tipe Data Umum #

Tipe data inti relasional berlaku nyaris seragam di semua produk; pemilihan yang tepat menentukan ukuran penyimpanan, presisi angka, dan query yang bisa dioptimalkan:

KategoriPostgreSQLSQLiteContoh isi
Bilangan bulatINTEGER, BIGINTINTEGER3500000 (harga)
Bilangan pecahan presisiNUMERIC(12,2)INTEGER + satuan sen125000.00
Pecahan binerREAL / DOUBLE PRECISIONREAL3.14159
TeksTEXT, VARCHAR(n)TEXT'Budi Santoso'
Tanggal-waktuDATE, TIMESTAMPTEXT ISO 8601'2024-06-01'
Logika benar-salahBOOLEANINTEGER 0/1TRUE

Uang sebaiknya disimpan sebagai NUMERIC atau integer berdenominasi sen, bukan REAL — pecahan biner tidak merepresentasikan 0.1 secara persis sehingga penjumlahan ribuan transaksi bisa bergeser beberapa rupiah. SQLite tidak punya tipe DATE asli; karena itu section ini menyimpan tanggal sebagai teks ISO YYYY-MM-DD yang tetap terurut benar secara leksikografis.

Satu keunikan SQLite: tipe kolom hanyalah rekomendasi (type affinity), bukan paksaan keras — kita uji:

python
koneksi.execute(
    "INSERT INTO pelanggan (id, nama, kota, saldo) VALUES (4, 'Andi Wijaya', NULL, '50000')"
)
for baris in koneksi.execute("SELECT nama, saldo, typeof(saldo) FROM pelanggan WHERE id = 4"):
    print(baris)

Output yang Dihasilkan:

text
('Andi Wijaya', 50000, 'integer')

Nilai '50000' dikirim sebagai string, tetapi SQLite mengonversinya ke integer karena kolom saldo berafiliasi INTEGER. PostgreSQL dan MySQL lebih ketat: kolom INTEGER menolak nilai di luar aturan tipe yang tegas.

DBMS Populer: PostgreSQL, MySQL, dan SQLite #

Perangkat lunak yang mengimplementasikan model relasional disebut RDBMS (Relational Database Management System). Ketiga nama berikut paling sering dijumpai di dunia kerja:

AspekPostgreSQLMySQLSQLite
Arsitekturclient-serverclient-serverpustaka tanpa server
Penyimpanansatu file per databasefile/folder per databasesatu file tunggal
Tipe data kayaya (NUMERIC, JSONB, array)sebagian (JSON)minimal, afinitas dinamis
Konkurensi tulistinggi (MVCC)tinggi (MVCC)satu penulis pada satu waktu
Khas untukaplikasi dan analitik seriusaplikasi web skala besaraplikasi mobile, analitik lokal, pengujian

SQLite hidup sebagai satu file yang dibaca langsung oleh proses aplikasi — pas untuk belajar dan pipeline file lokal. PostgreSQL dan MySQL berjalan sebagai proses server terpisah sehingga banyak klien bisa terhubung serentak, dengan NUMERIC presisi penuh untuk uang dan tipe tanggal asli. Dokumentasi resminya layak dibiasakan: dokumentasi SQLite dan dokumentasi PostgreSQL .

Model relasional memberi kita kotak berisi baris dan kolom. Pertanyaan berikutnya adalah keputusan desain: entitas apa yang jadi tabel, properti apa yang jadi kolom, dan bagaimana tabel saling terhubung — materi chapter Pemodelan Data.