Toko online menyimpan data pelanggan, produk, dan pesanan dalam tabel yang saling berhubungan. Tim pemasaran ingin tahu pelanggan mana yang paling sering berbelanja; tim keuangan membutuhkan total pendapatan. Semua jawaban bergantung pada keterampilan inti data engineer: menulis query SQL. Chapter ini memperkenalkan SQL sekaligus membangun skema toko online untuk seluruh chapter Dasar SQL.

Apa Itu SQL #

SQL (Structured Query Language) adalah bahasa standar untuk mengakses dan mengelola data pada database relasional — database berisi tabel baris-kolom yang saling terhubung lewat kunci seperti pelanggan_id. SQL lahir di IBM era 1970an dan distandardisasi ANSI/ISO sehingga dipahami hampir semua database modern.

Sifat terpenting SQL adalah deklaratif: kita mendeskripsikan hasil yang diinginkan, bukan langkah pengambilannya. Mencari produk termurah dengan Python berarti menulis perulangan dan membandingkan harga satu per satu; dalam SQL, cukup satu pernyataan:

sql
SELECT nama, harga FROM produk ORDER BY harga ASC LIMIT 1

Komponen query planner pada database yang memilih langkah eksekusi paling efisien — pemisahan “apa” dari “bagaimana” inilah yang membuat SQL bertahan setengah abad.

Kategori Perintah SQL #

Perintah SQL dikelompokkan berdasarkan perannya terhadap database:

KategoriPerintah utamaPeran
Data Definition Language (DDL)CREATE, ALTER, DROPmendefinisikan struktur tabel
Data Manipulation Language (DML)INSERT, UPDATE, DELETEmengubah isi tabel
Data Query Language (DQL)SELECTmembaca dan menganalisis data

Ada pula DCL untuk hak akses dan TCL untuk transaksi, dibahas pada chapter Transaksi Database. Chapter ini memakai DDL dan DML; DQL menyusul di chapter berikutnya.

SQL Standar dan Dialeknya #

Standar ANSI/ISO menjamin inti bahasa — SELECT, WHERE, JOIN, agregasi — portabel antar database. Di luar inti, tiap produk punya dialek: pembatasan baris memakai LIMIT di SQLite, PostgreSQL, dan MySQL, tetapi TOP di SQL Server.

Section ini memakai SQLite lewat modul sqlite3 Python — ringan dan tanpa server. Semua query memakai SQL standar; perilaku khusus PostgreSQL selalu disebut eksplisit. Rujukan resmi: dokumentasi PostgreSQL dan dokumentasi SQLite .

Membuat Skema Toko Online #

Skema toko online terdiri atas empat tabel: pelanggan (identitas pembeli), produk (daftar barang), pesanan (transaksi), dan detail_pesanan (item pesanan). Relasi antar tabel tampak seperti berikut:

Satu pelanggan boleh punya banyak pesanan, dan satu pesanan boleh memuat banyak produk — keduanya dijembatani tabel detail_pesanan. Berikut DDL pembangunnya:

sql
CREATE TABLE pelanggan (
    id INTEGER PRIMARY KEY,
    nama TEXT NOT NULL,
    kota TEXT,
    tanggal_daftar TEXT NOT NULL
);

CREATE TABLE produk (
    id INTEGER PRIMARY KEY,
    nama TEXT NOT NULL,
    kategori TEXT NOT NULL,
    harga INTEGER NOT NULL
);

CREATE TABLE pesanan (
    id INTEGER PRIMARY KEY,
    pelanggan_id INTEGER NOT NULL,
    tanggal TEXT NOT NULL,
    status TEXT NOT NULL,
    FOREIGN KEY (pelanggan_id) REFERENCES pelanggan (id)
);

CREATE TABLE detail_pesanan (
    pesanan_id INTEGER NOT NULL,
    produk_id INTEGER NOT NULL,
    jumlah INTEGER NOT NULL,
    subtotal INTEGER,
    PRIMARY KEY (pesanan_id, produk_id),
    FOREIGN KEY (pesanan_id) REFERENCES pesanan (id),
    FOREIGN KEY (produk_id) REFERENCES produk (id)
);

Mari kita telaah klausa-klausa penting pada DDL di atas:

  1. id INTEGER PRIMARY KEY: kolom id menjadi pengenal unik baris; SQLite mengisinya otomatis ketika tidak disebut pada INSERT.
  2. nama TEXT NOT NULL: batasan NOT NULL menolak baris tanpa nilai — setiap produk wajib bernama.
  3. kota TEXT: tanpa NOT NULL, kolom kota boleh NULL — berguna saat pelanggan belum melengkapi profil.
  4. FOREIGN KEY (pelanggan_id) REFERENCES pelanggan (id): memastikan setiap pesanan merujuk ke pelanggan yang benar-benar ada.
  5. PRIMARY KEY (pesanan_id, produk_id): kunci komposit — kombinasi keduanya selalu unik, satu pesanan memuat satu produk hanya sekali.

Mengisi Data dengan INSERT #

Setelah struktur siap, DML mengisi tabel dengan data fiktif — Budi dan Rina (Jakarta), Siti dan Farhan (Bandung), Dewi (Surabaya), serta Andi yang belum mengisi kota. Satu INSERT dapat memuat banyak baris:

sql
INSERT INTO pelanggan (id, nama, kota, tanggal_daftar) VALUES
    (1, 'Budi Santoso', 'Jakarta', '2024-01-15'),
    (2, 'Siti Rahayu', 'Bandung', '2024-02-03'),
    (3, 'Dewi Lestari', 'Surabaya', '2024-03-21'),
    (4, 'Rina Kartika', 'Jakarta', '2024-04-18'),
    (5, 'Andi Wijaya', NULL, '2024-05-02'),
    (6, 'Farhan Pratama', 'Bandung', '2024-06-25');

INSERT INTO produk (id, nama, kategori, harga) VALUES
    (1, 'Smartphone X1', 'elektronik', 3500000),
    (2, 'Laptop Pro 14', 'elektronik', 12500000),
    (3, 'TWS Earbuds', 'elektronik', 750000),
    (4, 'Kemeja Batik', 'fashion', 250000),
    (5, 'Dress Summer', 'fashion', 380000),
    (6, 'Sneakers Klasik', 'fashion', 900000);

INSERT INTO pesanan (id, pelanggan_id, tanggal, status) VALUES
    (1, 1, '2024-06-01', 'selesai'),
    (2, 2, '2024-06-03', 'selesai'),
    (3, 1, '2024-06-10', 'dikirim'),
    (4, 3, '2024-06-15', 'selesai'),
    (5, 4, '2024-06-20', 'dibatalkan'),
    (6, 2, '2024-07-01', 'diproses'),
    (7, 5, '2024-07-05', 'selesai'),
    (8, 3, '2024-07-12', 'diproses'),
    (9, 1, '2024-07-20', 'selesai');

INSERT INTO detail_pesanan (pesanan_id, produk_id, jumlah, subtotal) VALUES
    (1, 1, 1, 3500000),
    (1, 4, 2, 500000),
    (2, 5, 1, 380000),
    (2, 3, 1, 750000),
    (3, 2, 1, 12500000),
    (4, 6, 1, 900000),
    (4, 4, 1, 250000),
    (5, 5, 1, NULL),
    (6, 3, 2, 1500000),
    (7, 6, 1, 900000),
    (9, 4, 1, 250000);

Daftar kolom setelah nama tabel menjaga urutan nilai eksplisit; NULL menyatakan nilai yang memang tidak tersedia. Tiga kejadian disiapkan untuk pembelajaran: kota Andi kosong, subtotal pesanan 5 yang dibatalkan tidak dihitung, dan pesanan 8 belum punya baris detail.

Menjalankan Query dengan Python #

Seluruh pernyataan di atas dijalankan sekali secara berurutan ke file database data.db. Setelah itu, setiap query pada section ini dijalankan dengan pola koneksi berikut:

python
import sqlite3

koneksi = sqlite3.connect("data.db")
kursor = koneksi.execute("SELECT * FROM pelanggan")
for baris in kursor.fetchall():
    print(baris)
koneksi.close()

Output yang Dihasilkan:

text
(1, 'Budi Santoso', 'Jakarta', '2024-01-15')
(2, 'Siti Rahayu', 'Bandung', '2024-02-03')
(3, 'Dewi Lestari', 'Surabaya', '2024-03-21')
(4, 'Rina Kartika', 'Jakarta', '2024-04-18')
(5, 'Andi Wijaya', None, '2024-05-02')
(6, 'Farhan Pratama', 'Bandung', '2024-06-25')

Setiap baris tampil sebagai tuple Python; NULL tampil sebagai None — kota Andi pada baris kelima memang kosong. Sebagai pemeriksaan akhir, hitung baris tiap tabel:

sql
SELECT 'pelanggan' AS tabel, COUNT(*) AS jumlah FROM pelanggan
UNION ALL
SELECT 'produk', COUNT(*) FROM produk
UNION ALL
SELECT 'pesanan', COUNT(*) FROM pesanan
UNION ALL
SELECT 'detail_pesanan', COUNT(*) FROM detail_pesanan

Output yang Dihasilkan:

text
('pelanggan', 6)
('produk', 6)
('pesanan', 9)
('detail_pesanan', 11)

Skema dan data toko online kini menjadi fondasi seluruh section ini. Kita sudah bisa membaca seluruh isi tabel; langkah berikutnya adalah mengambil tepat kolom dan baris yang dibutuhkan — materi chapter SELECT dan Filtering.