Materi pada Chapter SQL Dasar untuk Python menunjukkan SQL bekerja dari Python, tetapi kita belum berkenalan dengan mesin di baliknya: SQLite. SQLite menyimpan seluruh database — tabel, index, data — dalam satu file biasa, tanpa proses server terpisah, dan Python membawa pendukungnya di standard library lewat module sqlite3. Artinya, tidak ada instalasi apa pun: import sqlite3 sudah cukup untuk database sungguhan. Pada chapter ini kita membedah module tersebut dari membuat file hingga membaca hasil query.

Database dalam Satu File #

Fungsi sqlite3.connect() menerima path file database. Kalau file belum ada, SQLite membuatnya:

python
import sqlite3
from pathlib import Path

Path("toko.db").unlink(missing_ok=True)
conn = sqlite3.connect("toko.db")
print("Versi SQLite:", sqlite3.sqlite_version)

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 (nama, harga, stok) VALUES ('Teh Celup Isi 25', 9500, 30)"
)
conn.commit()
print("File database dibuat:", Path("toko.db").exists())
print("Header file:", Path("toko.db").read_bytes()[:15])
conn.close()

Output yang Dihasilkan:

text
Versi SQLite: 3.53.1
File database dibuat: True
Header file: b'SQLite format 3'

Mari kita telaah baris-baris pentingnya:

  1. Baris 4 (Path("toko.db").unlink(missing_ok=True)): menghapus file lama agar contoh selalu mulai dari nol.
  2. Baris 5 (conn = sqlite3.connect("toko.db")): membuka koneksi; karena toko.db belum ada, SQLite sekaligus membuat filenya.
  3. Baris 6 (sqlite3.sqlite_version): menampilkan versi pustaka C SQLite yang tertanam di interpreter Python — pada mesin kami 3.53.1 dengan Python 3.12; angka di komputer Anda bisa berbeda sesuai versi interpreter.
  4. Baris 18 (print("File database dibuat:", ...)): membuktikan file toko.db benar-benar ada di disk setelah connect() dan commit().
  5. Baris 19 (Path("toko.db").read_bytes()[:15]): membaca 15 byte pertama file — string SQLite format 3 adalah awal tanda tangan resmi setiap file database SQLite.

Database Sementara di Memori #

Path khusus ":memory:" membuat database yang hidup sepenuhnya di RAM — tanpa file, tanpa jejak, hilang saat koneksi ditutup. Untuk perhitungan sekali jalan, ini menghemat kerja membersihkan file sementara:

python
import sqlite3

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE cuaca (kota TEXT, suhu REAL)")
conn.executemany(
    "INSERT INTO cuaca (kota, suhu) VALUES (?, ?)",
    [("Jakarta", 31.5), ("Bandung", 24.0), ("Medan", 30.2)],
)
print(conn.execute("SELECT ROUND(AVG(suhu), 2) FROM cuaca").fetchone())
conn.close()

Output yang Dihasilkan:

text
(28.57,)

Tiga kota masuk, satu nilai rata-rata keluar, dan tidak ada file yang perlu dihapus — SQLite memang menyediakan AVG bawaan sehingga perhitungan berlangsung di dalam database. Pola :memory: juga populer di test otomatis: tiap test mendapat database segar yang cepat dan terisolasi.

Membaca Hasil Query #

Hasil query datang sebagai baris bertipe tuple. Dua method utama untuk mengambilnya adalah fetchone() (satu baris, atau None kalau habis) dan fetchall() (semua sisa baris):

python
import sqlite3

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE produk (id INTEGER PRIMARY KEY, nama TEXT, harga REAL, stok INTEGER)")
conn.executemany(
    "INSERT INTO produk (nama, harga, stok) VALUES (?, ?, ?)",
    [
        ("Kopi Hitam 200g", 18000, 12),
        ("Teh Celup Isi 25", 9500, 30),
        ("Gula Pasir 1kg", 15500, 25),
        ("Beras Premium 5kg", 68000, 8),
        ("Susu UHT 1L", 19000, 15),
    ],
)
conn.commit()

cursor = conn.execute("SELECT nama, stok FROM produk ORDER BY nama")
print("fetchone :", cursor.fetchone())
print("fetchall :", cursor.fetchall())
conn.close()

Output yang Dihasilkan:

text
fetchone : ('Beras Premium 5kg', 8)
fetchall : [('Gula Pasir 1kg', 25), ('Kopi Hitam 200g', 12), ('Susu UHT 1L', 15), ('Teh Celup Isi 25', 30)]

Urutan baris dijamin karena ORDER BY nama; tanpa klausa tersebut, urutan baris yang dikembalikan SQLite tidak dijanjikan. Perbedaan perilaku fetchone, fetchmany, dan fetchall — termasuk posisi cursor yang maju — kita bahas rinci pada Chapter Koneksi dan Cursor.

Akses Kolom Bernama dengan sqlite3.Row #

Mengakses kolom lewat indeks angka rawan salah urut: baris[3] tidak menyiratkan stok. Mengatur conn.row_factory = sqlite3.Row membuat setiap baris bisa dibaca berdasarkan nama kolom:

python
import sqlite3

conn = sqlite3.connect(":memory:")
conn.row_factory = sqlite3.Row
conn.execute("CREATE TABLE produk (id INTEGER PRIMARY KEY, nama TEXT, harga REAL, stok INTEGER)")
conn.executemany(
    "INSERT INTO produk (nama, harga, stok) VALUES (?, ?, ?)",
    [("Kopi Hitam 200g", 18000, 12), ("Gula Pasir 1kg", 15500, 25)],
)
conn.commit()

baris = conn.execute("SELECT * FROM produk ORDER BY nama").fetchone()
print(baris["nama"], "stok", baris["stok"])
print("Kolom tersedia:", baris.keys())
conn.close()

Output yang Dihasilkan:

text
Gula Pasir 1kg stok 25
Kolom tersedia: ['id', 'nama', 'harga', 'stok']

Objek sqlite3.Row tetap mendukung indeks angka (baris[1]) sekaligus akses bernama (baris["nama"]), dan method keys() menampilkan daftar nama kolom — berguna saat men-debug query yang panjang.

Sebagai penutup: seluruh operasi CRUD yang sesungguhnya — program inventaris utuh dengan fungsi-fungsi terstruktur — akan kita bangun pada Chapter CRUD dengan SQLite, dan menjadi proyek besar pada Proyek Aplikasi Inventaris SQLite di bagian akhir course.