Data operasional perusahaan umumnya tinggal di dalam database: transaksi penjualan di PostgreSQL, catatan pelanggan di MySQL, atau log aktivitas di SQL Server. Menyalin isi database ke CSV setiap kali analisis membutuhkan waktu dan menghasilkan salinan yang cepat kedaluwarsa. Fungsi pd.read_sql() menyelesaikan masalah ini dengan mengeksekusi query SQL lalu mengembalikan hasilnya sebagai DataFrame, sehingga filter dan agregasi lebih lanjut tetap bisa dilakukan di pandas. Chapter ini membahas peran SQLAlchemy sebagai jembatan, membangun database SQLite untuk latihan, dan mengulas ragam fungsi pembaca SQL yang disediakan pandas.
SQLAlchemy sebagai Jembatan Database #
read_sql() tidak berbicara langsung dengan setiap jenis database. Pandas menyerahkan urusan koneksi kepada SQLAlchemy, library yang menyediakan lapisan akses seragam untuk banyak database melalui konsep engine. Engine dibuat dengan create_engine() dan URL koneksi berformat <dialect>+<driver>://<pengguna>:<kata sandi>@<host>/<nama_db>. Beberapa contoh URL yang sering dipakai:
| Database | Contoh URL SQLAlchemy |
|---|---|
| SQLite (file) | sqlite:///penjualan.db |
| SQLite (memori) | sqlite:///:memory: |
| PostgreSQL | postgresql+psycopg://analisis:YOUR_DATABASE_PASSWORD@localhost:5432/gudang |
| MySQL | mysql+pymysql://analisis:YOUR_DATABASE_PASSWORD@localhost:3306/gudang |
Untuk SQLite, URL cukup berisi path file database sehingga tidak ada server yang perlu dijalankan. Untuk database server, bagian YOUR_DATABASE_PASSWORD sebaiknya dibaca dari environment variable atau file konfigurasi, bukan ditulis langsung di kode. Pandas juga masih menerima objek sqlite3.Connection bawaan Python, tetapi SQLAlchemy adalah pilihan yang lebih baik karena pola kodenya sama untuk semua database; dokumentasi SQLAlchemy
menjelaskan konsep engine secara mendalam.
Menyiapkan Database SQLite untuk Latihan #
Lingkungan latihan Course ini tidak memiliki server PostgreSQL atau MySQL, sedangkan SQLite justru ideal untuk belajar: satu file, tanpa instalasi, dan didukung modul sqlite3 bawaan Python. Skrip berikut membuat tabel transaksi berisi empat baris, lalu membacanya kembali lewat read_sql() dengan tiga cara berbeda. File database dibuat saat runtime dan dihapus di akhir skrip agar bundle tetap bersih.
import sqlite3
from pathlib import Path
import pandas as pd
from sqlalchemy import create_engine
db_path = Path("penjualan.db")
koneksi = sqlite3.connect(db_path)
koneksi.execute(
"CREATE TABLE transaksi (id INTEGER PRIMARY KEY, kasir TEXT, kota TEXT, total INTEGER)"
)
data = [
(1, "Budi", "Bandung", 150000),
(2, "Siti", "Jakarta", 230000),
(3, "Dewi", "Surabaya", 98000),
(4, "Rian", "Bandung", 175000),
]
koneksi.executemany("INSERT INTO transaksi VALUES (?, ?, ?, ?)", data)
koneksi.commit()
koneksi.close()
engine = create_engine(f"sqlite:///{db_path}")
df = pd.read_sql("SELECT kasir, kota, total FROM transaksi", engine)
print(df)
print()
print(df.dtypes)
print()
df_bandung = pd.read_sql(
"SELECT kasir, total FROM transaksi WHERE kota = ?", engine, params=("Bandung",)
)
print(df_bandung)
print()
df_tabel = pd.read_sql_table("transaksi", engine)
print("Kolom read_sql_table:", list(df_tabel.columns))
print("Index read_sql_table:", list(df_tabel.index))
engine.dispose()
db_path.unlink()
print("File database sementara sudah dihapus:", not db_path.exists())Output yang Dihasilkan:
kasir kota total
0 Budi Bandung 150000
1 Siti Jakarta 230000
2 Dewi Surabaya 98000
3 Rian Bandung 175000
kasir str
kota str
total int64
dtype: object
kasir total
0 Budi 150000
1 Rian 175000
Kolom read_sql_table: ['id', 'kasir', 'kota', 'total']
Index read_sql_table: [0, 1, 2, 3]
File database sementara sudah dihapus: TrueBaris demi baris, inilah yang terjadi:
- Baris 7-20: modul
sqlite3membuka filepenjualan.db, membuat tabeltransaksi, mengisi empat baris denganexecutemany(), lalu menutup koneksi. - Baris 22 (
engine = create_engine(...)): SQLAlchemy engine menunjuk file yang sama; engine baru membuka koneksi saat operasi pertama berjalan. - Baris 24-27:
read_sql()mengeksekusi stringSELECTdan mengembalikan DataFrame; kolomkasirdankotabertipestr, kolomtotalbertipeint64. - Baris 30-33: query memakai tanda
?denganparams=("Bandung",); nilai parameter dikirim ke driver secara terpisah sehingga aman dari SQL injection. - Baris 36-38:
read_sql_table()membaca seluruh isi tabel tanpa menulis SQL, dan semua kolom termasukidikut terbawa. - Baris 40-42:
engine.dispose()menutup pool koneksi, file database latihan dihapus, dan keberhasilan pembersihan dikonfirmasi.
read_sql, read_sql_query, dan read_sql_table #
Pandas menyediakan tiga pintu masuk dengan peran yang saling melengkapi. Fungsi pandas.read_sql
adalah pintu umum yang menerima string query maupun nama tabel lalu menyerahkannya ke fungsi yang sesuai. Fungsi read_sql_query() khusus untuk perintah SELECT yang hasilnya berbentuk tabel, sedangkan fungsi pandas.read_sql_table
mengambil seluruh isi satu tabel beserta semua kolomnya. Untuk tabel berukuran besar, parameter chunksize=n membuat fungsi mengembalikan iterator yang menghasilkan DataFrame berisi n baris per panggilan, sehingga memori tidak habis oleh satu DataFrame raksasa.
Satu detail yang sering ditanyakan: kolom id di database tidak otomatis menjadi index DataFrame. Hasil read_sql() memakai RangeIndex biasa; bila kolom id memang ingin dijadikan index, tambahkan parameter index_col="id".
Membaca data dari database baru separuh dari pekerjaan; sering kali hasil analisis juga harus ditulis kembali ke database agar bisa dipakai aplikasi atau rekan tim. Chapter berikutnya membahas to_sql() beserta tiga mode penulisannya.