1. Pengenalan SQL & Database
SQL (Structured Query Language) adalah bahasa standar untuk berinteraksi dengan database relasional. SQL digunakan untuk membuat, membaca, memperbarui, dan menghapus data — atau yang sering disingkat CRUD (Create, Read, Update, Delete).
SQL pertama kali dikembangkan oleh IBM pada tahun 1970-an dan telah menjadi standar industri. Hampir semua database modern mendukung SQL — dari yang kecil seperti SQLite hingga yang besar seperti PostgreSQL, MySQL, SQL Server, dan Oracle.
Apa Itu Database Relasional?
Database relasional menyimpan data dalam bentuk tabel (baris dan kolom) yang saling berhubungan melalui relasi. Setiap tabel merepresentasikan satu entitas (misalnya: pelanggan, produk, pesanan), setiap baris adalah satu record, dan setiap kolom adalah satu atribut.
| Istilah | Penjelasan | Contoh |
|---|---|---|
| Database | Kumpulan tabel yang saling berhubungan | database toko_online |
| Tabel | Struktur data baris-kolom | tabel pelanggan |
| Baris (Row) | Satu record data | Satu pelanggan spesifik |
| Kolom (Column) | Atribut/field dari data | nama, email, telepon |
| Primary Key | ID unik untuk setiap baris | id_pelanggan |
| Foreign Key | Referensi ke tabel lain | id_produk di tabel pesanan |
Jenis Perintah SQL
| Kategori | Perintah | Fungsi |
|---|---|---|
| DDL (Data Definition) | CREATE, ALTER, DROP | Membuat/mengubah/menghapus struktur tabel |
| DML (Data Manipulation) | SELECT, INSERT, UPDATE, DELETE | Membaca/menambah/mengubah/menghapus data |
| DCL (Data Control) | GRANT, REVOKE | Mengatur hak akses pengguna |
| TCL (Transaction) | COMMIT, ROLLBACK | Mengatur transaksi database |
┌─────────────────────────────────────────────────────────────────┐ │ DATABASE: toko_online │ │ │ │ ┌──────────────────┐ ┌──────────────────────────┐ │ │ │ pelanggan │ │ pesanan │ │ │ ├──────────────────┤ ├──────────────────────────┤ │ │ │ id_pelanggan (PK)│◄────────│ id_pelanggan (FK) │ │ │ │ nama │ │ id_pesanan (PK) │ │ │ │ email │ │ tanggal │ │ │ │ telepon │ │ total │ │ │ │ kota │ │ status │ │ │ └──────────────────┘ └──────────┬───────────────┘ │ │ │ │ │ │ │ │ ┌──────────────────┐ ┌──────────┴───────────────┐ │ │ │ produk │ │ detail_pesanan │ │ │ ├──────────────────┤ ├──────────────────────────┤ │ │ │ id_produk (PK) │◄────────│ id_produk (FK) │ │ │ │ nama_produk │ │ id_pesanan (FK) │ │ │ │ harga │ │ jumlah │ │ │ │ stok │ │ subtotal │ │ │ │ kategori │ └──────────────────────────┘ │ │ └──────────────────┘ │ └─────────────────────────────────────────────────────────────────┘
Setup Environment
# ===== SQLite (paling ringan, cocok untuk belajar) ===== # Sudah terinstal di kebanyakan sistem Linux/macOS sqlite3 --version # Buat database baru sqlite3 toko_online.db # ===== MySQL ===== # Ubuntu/Debian: sudo apt install mysql-server sudo mysql_secure_installation mysql -u root -p # ===== PostgreSQL ===== # Ubuntu/Debian: sudo apt install postgresql postgresql-client sudo -u postgres psql # ===== Online (tanpa instalasi) ===== # https://sqliteonline.com — SQLite, MySQL, PostgreSQL # https://db-fiddle.com — MySQL, PostgreSQL, SQLite
2. CREATE TABLE — Membuat Tabel
CREATE TABLE digunakan untuk membuat tabel baru di database. Di dalam definisi tabel, kita menentukan nama kolom, tipe data, dan constraint (aturan).
-- =============================================
-- MEMBUAT DATABASE
-- =============================================
CREATE DATABASE IF NOT EXISTS toko_online;
USE toko_online;
-- =============================================
-- TABEL: pelanggan
-- =============================================
CREATE TABLE pelanggan (
id_pelanggan INT PRIMARY KEY AUTO_INCREMENT,
nama VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
telepon VARCHAR(20),
kota VARCHAR(50) DEFAULT 'Jakarta',
tanggal_daftar DATE DEFAULT (CURRENT_DATE),
is_active BOOLEAN DEFAULT TRUE
);
-- =============================================
-- TABEL: produk
-- =============================================
CREATE TABLE produk (
id_produk INT PRIMARY KEY AUTO_INCREMENT,
nama_produk VARCHAR(200) NOT NULL,
harga DECIMAL(12, 2) NOT NULL CHECK (harga > 0),
stok INT NOT NULL DEFAULT 0 CHECK (stok >= 0),
kategori VARCHAR(50),
deskripsi TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- =============================================
-- TABEL: pesanan (dengan foreign key)
-- =============================================
CREATE TABLE pesanan (
id_pesanan INT PRIMARY KEY AUTO_INCREMENT,
id_pelanggan INT NOT NULL,
tanggal_pesanan DATETIME DEFAULT CURRENT_TIMESTAMP,
total DECIMAL(12, 2) NOT NULL,
status ENUM('pending', 'diproses', 'dikirim', 'selesai', 'batal')
DEFAULT 'pending',
catatan TEXT,
-- Foreign key: relasi ke tabel pelanggan
FOREIGN KEY (id_pelanggan) REFERENCES pelanggan(id_pelanggan)
ON DELETE RESTRICT
ON UPDATE CASCADE
);
-- =============================================
-- TABEL: detail_pesanan (junction table)
-- =============================================
CREATE TABLE detail_pesanan (
id_detail INT PRIMARY KEY AUTO_INCREMENT,
id_pesanan INT NOT NULL,
id_produk INT NOT NULL,
jumlah INT NOT NULL CHECK (jumlah > 0),
harga_satuan DECIMAL(12, 2) NOT NULL,
subtotal DECIMAL(12, 2) GENERATED ALWAYS AS (jumlah * harga_satuan) STORED,
FOREIGN KEY (id_pesanan) REFERENCES pesanan(id_pesanan) ON DELETE CASCADE,
FOREIGN KEY (id_produk) REFERENCES produk(id_produk) ON DELETE RESTRICT
);
Tipe Data Umum
| Tipe Data | Deskripsi | Contoh Nilai |
|---|---|---|
| INT / INTEGER | Bilangan bulat | 42, -1, 0 |
| DECIMAL(p,s) | Bilangan desimal dengan presisi | 15000000.50 |
| VARCHAR(n) | Teks dengan panjang maksimum n | 'Budi Santoso' |
| TEXT | Teks panjang tanpa batas | 'Lorem ipsum dolor sit amet...' |
| DATE | Tanggal (tanpa waktu) | '2026-06-25' |
| DATETIME / TIMESTAMP | Tanggal dan waktu | '2026-06-25 14:30:00' |
| BOOLEAN | True / False | TRUE, FALSE |
| ENUM('a','b') | Pilihan nilai tetap | 'pending', 'selesai' |
Constraint Umum
| Constraint | Fungsi |
|---|---|
| PRIMARY KEY | Identifikasi unik setiap baris — tidak boleh NULL dan tidak boleh duplikat |
| FOREIGN KEY | Relasi ke tabel lain — menjaga integritas referensial |
| NOT NULL | Kolom wajib diisi — tidak boleh kosong |
| UNIQUE | Nilai harus unik — tidak boleh ada duplikat |
| DEFAULT | Nilai default jika tidak diisi |
| CHECK | Validasi kondisi tertentu (misal: harga > 0) |
| AUTO_INCREMENT | Otomatis menambah ID (MySQL) / SERIAL (PostgreSQL) |
3. SELECT — Mengambil Data
SELECT adalah perintah paling sering digunakan dalam SQL — untuk mengambil (membaca) data dari tabel. Ini termasuk dalam kategori DML (Data Manipulation Language).
-- =============================================
-- SELECT: Mengambil semua kolom
-- =============================================
SELECT * FROM pelanggan;
-- =============================================
-- SELECT: Mengambil kolom tertentu
-- =============================================
SELECT nama, email, kota FROM pelanggan;
-- =============================================
-- SELECT dengan alias (AS)
-- =============================================
SELECT
nama AS nama_lengkap,
email AS alamat_email,
tanggal_daftar AS tgl_daftar
FROM pelanggan;
-- =============================================
-- SELECT DISTINCT: Hapus duplikat
-- =============================================
-- Ambil daftar kota unik dari pelanggan
SELECT DISTINCT kota FROM pelanggan;
-- Kombinasi distinct
SELECT DISTINCT kota, is_active FROM pelanggan;
-- =============================================
-- SELECT dengan LIMIT dan OFFSET
-- =============================================
-- Ambil 5 produk pertama
SELECT nama_produk, harga, stok
FROM produk
LIMIT 5;
-- Pagination: ambil data halaman 2 (10 per halaman)
SELECT nama_produk, harga
FROM produk
ORDER BY nama_produk
LIMIT 10 OFFSET 10;
-- =============================================
-- Menghitung tanpa mengambil data
-- =============================================
-- Hitung total pelanggan
SELECT COUNT(*) AS total_pelanggan FROM pelanggan;
-- Hitung pelanggan aktif saja
SELECT COUNT(*) AS pelanggan_aktif
FROM pelanggan
WHERE is_active = TRUE;
Dalam production, hindari SELECT * karena mengambil semua kolom termasuk yang tidak dibutuhkan. Ini boros bandwidth dan memori. Sebutkan kolom yang Anda butuhkan secara eksplisit: SELECT nama, email FROM pelanggan.
4. WHERE — Filter Data
Clause WHERE digunakan untuk memfilter baris berdasarkan kondisi tertentu. Hanya baris yang memenuhi kondisi yang akan dikembalikan.
-- =============================================
-- Operator Perbandingan
-- =============================================
-- Sama dengan (=)
SELECT * FROM produk WHERE kategori = 'Elektronik';
-- Tidak sama dengan (!= atau <>)
SELECT * FROM produk WHERE kategori != 'Makanan';
-- Lebih besar / lebih kecil
SELECT * FROM produk WHERE harga > 1000000;
SELECT * FROM produk WHERE harga <= 500000;
SELECT * FROM produk WHERE stok BETWEEN 10 AND 100;
-- =============================================
-- Operator Logika: AND, OR, NOT
-- =============================================
-- AND: semua kondisi harus terpenuhi
SELECT * FROM produk
WHERE harga > 1000000 AND stok > 0 AND kategori = 'Elektronik';
-- OR: salah satu kondisi terpenuhi
SELECT * FROM pelanggan
WHERE kota = 'Jakarta' OR kota = 'Bandung' OR kota = 'Surabaya';
-- NOT: membalik kondisi
SELECT * FROM produk
WHERE NOT kategori = 'Makanan';
-- =============================================
-- IN: Cek nilai dalam daftar
-- =============================================
-- Sama dengan OR tapi lebih ringkas
SELECT * FROM pelanggan
WHERE kota IN ('Jakarta', 'Bandung', 'Surabaya', 'Yogyakarta');
-- NOT IN: kecualikan daftar
SELECT * FROM produk
WHERE kategori NOT IN ('Rokok', 'Alkohol');
-- =============================================
-- LIKE: Pencarian pola (pattern matching)
-- =============================================
-- % = nol atau banyak karakter
-- _ = tepat satu karakter
-- Nama diawali huruf 'B'
SELECT * FROM pelanggan WHERE nama LIKE 'B%';
-- Nama mengandung 'san'
SELECT * FROM pelanggan WHERE nama LIKE '%san%';
-- Email diakhiri '@gmail.com'
SELECT * FROM pelanggan WHERE email LIKE '%@gmail.com';
-- Nama tepat 5 karakter
SELECT * FROM pelanggan WHERE nama LIKE '_____';
-- =============================================
-- IS NULL / IS NOT NULL
-- =============================================
SELECT * FROM pelanggan WHERE telepon IS NULL;
SELECT * FROM pelanggan WHERE telepon IS NOT NULL;
-- =============================================
-- Kombinasi kompleks
-- =============================================
SELECT nama_produk, harga, stok, kategori
FROM produk
WHERE kategori IN ('Elektronik', 'Aksesoris')
AND harga BETWEEN 100000 AND 5000000
AND stok > 0
AND nama_produk LIKE '%USB%'
ORDER BY harga ASC;
Tabel Referensi Operator WHERE
| Operator | Deskripsi | Contoh |
|---|---|---|
| = | Sama dengan | kota = 'Jakarta' |
| != / <> | Tidak sama dengan | status != 'batal' |
| >, <, >=, <= | Perbandingan | harga >= 1000000 |
| BETWEEN | Rentang nilai (inklusif) | harga BETWEEN 100 AND 500 |
| IN | Dalam daftar nilai | kota IN ('JKT', 'BDG') |
| LIKE | Pencarian pola | nama LIKE 'B%' |
| IS NULL | Cek nilai kosong | telepon IS NULL |
| AND, OR, NOT | Operator logika | A AND B OR C |
5. ORDER BY — Mengurutkan Data
Clause ORDER BY digunakan untuk mengurutkan hasil query berdasarkan satu atau lebih kolom. Arah pengurutan: ASC (ascending/naik, default) atau DESC (descending/turun).
-- =============================================
-- ORDER BY dasar
-- =============================================
-- Urutkan produk berdasarkan harga (termurah ke termahal)
SELECT nama_produk, harga, stok
FROM produk
ORDER BY harga ASC;
-- Urutkan berdasarkan harga (termahal ke termurah)
SELECT nama_produk, harga, stok
FROM produk
ORDER BY harga DESC;
-- =============================================
-- ORDER BY dengan beberapa kolom
-- =============================================
-- Urutkan berdasarkan kategori (A-Z), lalu harga (termahal dulu)
SELECT nama_produk, kategori, harga
FROM produk
ORDER BY kategori ASC, harga DESC;
-- =============================================
-- ORDER BY dengan LIMIT — ambil Top N
-- =============================================
-- 5 produk termahal
SELECT nama_produk, harga
FROM produk
ORDER BY harga DESC
LIMIT 5;
-- 3 pelanggan paling baru
SELECT nama, email, tanggal_daftar
FROM pelanggan
ORDER BY tanggal_daftar DESC
LIMIT 3;
-- =============================================
-- ORDER BY dengan expression
-- =============================================
-- Urutkan berdasarkan total nilai stok
SELECT nama_produk, harga, stok,
(harga * stok) AS nilai_stok
FROM produk
ORDER BY nilai_stok DESC;
-- =============================================
-- ORDER BY dengan NULL
-- =============================================
-- NULL dianggap paling kecil di ASC, paling besar di DESC
-- PostgreSQL: bisa atur posisi NULL
SELECT nama, telepon
FROM pelanggan
ORDER BY telepon ASC NULLS LAST; -- PostgreSQL syntax
-- MySQL: gunakan IFNULL / COALESCE
SELECT nama, telepon
FROM pelanggan
ORDER BY COALESCE(telepon, 'ZZZ') ASC;
6. JOIN — Menggabungkan Tabel
JOIN digunakan untuk menggabungkan data dari dua atau lebih tabel berdasarkan kolom yang saling berhubungan (biasanya foreign key). Ini adalah salah satu fitur paling powerful dari database relasional.
Tabel A Tabel B
┌───────┐ ┌───────┐
│ A │ │ B │
│ ┌───┐ │ │ ┌───┐ │
│ │ 1 │ │ │ │ 1 │ │
│ │ 2 │─┼──┐ ┌─┼─│ 2 │ │
│ │ 3 │ │ │ │ │ │ 3 │ │
│ │ 4 │─┼──┼──┼─│ │ 4 │ │
│ └───┘ │ │ │ │ └───┘ │
└───────┘ │ │ └───────┘
│ │
INNER JOIN: 2, 3, 4 (irisan)
LEFT JOIN: 1, 2, 3, 4 (semua A)
RIGHT JOIN: 2, 3, 4 (semua B)
FULL JOIN: 1, 2, 3, 4 (semua A + B)
-- =============================================
-- INNER JOIN: Hanya baris yang cocok di kedua tabel
-- =============================================
-- Ambil pesanan beserta nama pelanggan
SELECT
p.id_pesanan,
pl.nama AS nama_pelanggan,
p.tanggal_pesanan,
p.total,
p.status
FROM pesanan p
INNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelanggan;
-- JOIN dengan filter
SELECT
p.id_pesanan,
pl.nama,
p.total,
p.status
FROM pesanan p
INNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelanggan
WHERE p.status = 'selesai'
AND p.total > 1000000;
-- =============================================
-- LEFT JOIN: Semua baris dari tabel kiri + yang cocok
-- =============================================
-- Tampilkan SEMUA pelanggan, meskipun belum pernah pesan
SELECT
pl.nama,
pl.email,
COUNT(p.id_pesanan) AS jumlah_pesanan,
COALESCE(SUM(p.total), 0) AS total_belanja
FROM pelanggan pl
LEFT JOIN pesanan p ON pl.id_pelanggan = p.id_pelanggan
GROUP BY pl.id_pelanggan, pl.nama, pl.email;
-- =============================================
-- RIGHT JOIN: Semua baris dari tabel kanan + yang cocok
-- =============================================
-- Tampilkan semua produk, termasuk yang belum pernah dipesan
SELECT
pr.nama_produk,
COALESCE(SUM(dp.jumlah), 0) AS total_terjual
FROM detail_pesanan dp
RIGHT JOIN produk pr ON dp.id_produk = pr.id_produk
GROUP BY pr.id_produk, pr.nama_produk;
-- =============================================
-- MULTIPLE JOIN: Gabungkan 3+ tabel
-- =============================================
-- Detail lengkap pesanan: pelanggan + pesanan + produk
SELECT
pl.nama AS pelanggan,
pr.nama_produk AS produk,
dp.jumlah,
dp.harga_satuan,
dp.subtotal,
p.tanggal_pesanan,
p.status
FROM pesanan p
INNER JOIN pelanggan pl ON p.id_pelanggan = pl.id_pelanggan
INNER JOIN detail_pesanan dp ON p.id_pesanan = dp.id_pesanan
INNER JOIN produk pr ON dp.id_produk = pr.id_produk
WHERE p.status != 'batal'
ORDER BY p.tanggal_pesanan DESC;
-- =============================================
-- SELF JOIN: Tabel bergabung dengan dirinya sendiri
-- =============================================
-- Contoh: cari pelanggan dari kota yang sama
SELECT
a.nama AS pelanggan_1,
b.nama AS pelanggan_2,
a.kota
FROM pelanggan a
INNER JOIN pelanggan b ON a.kota = b.kota
WHERE a.id_pelanggan < b.id_pelanggan;
Ringkasan Jenis JOIN
| Jenis JOIN | Yang Dikembalikan | Kapan Digunakan |
|---|---|---|
| INNER JOIN | Hanya baris yang cocok di kedua tabel | Data yang pasti punya relasi |
| LEFT JOIN | Semua baris kiri + yang cocok dari kanan | Ingin tampilkan semua data utama |
| RIGHT JOIN | Semua baris kanan + yang cocok dari kiri | Jarang dipakai — bisa ditulis sebagai LEFT JOIN |
| FULL JOIN | Semua baris dari kedua tabel | Ingin semua data dari kedua sisi |
| CROSS JOIN | Kombinasi semua baris (cartesian product) | Kombinasi / matrix data |
7. GROUP BY — Agregasi Data
GROUP BY digunakan untuk mengelompokkan baris yang memiliki nilai sama di satu atau lebih kolom, lalu menghitung ringkasan (agregasi) untuk setiap grup menggunakan fungsi seperti COUNT(), SUM(), AVG(), MIN(), MAX().
-- =============================================
-- Fungsi Agregasi Dasar
-- =============================================
-- Hitung total pelanggan
SELECT COUNT(*) AS total FROM pelanggan;
-- Rata-rata harga produk
SELECT AVG(harga) AS rata_rata_harga FROM produk;
-- Total pendapatan dari semua pesanan selesai
SELECT SUM(total) AS total_pendapatan
FROM pesanan
WHERE status = 'selesai';
-- Harga termurah dan termahal
SELECT MIN(harga) AS termurah, MAX(harga) AS termahal
FROM produk;
-- =============================================
-- GROUP BY: Kelompokkan berdasarkan kolom
-- =============================================
-- Jumlah pelanggan per kota
SELECT
kota,
COUNT(*) AS jumlah_pelanggan
FROM pelanggan
GROUP BY kota
ORDER BY jumlah_pelanggan DESC;
-- Statistik produk per kategori
SELECT
kategori,
COUNT(*) AS jumlah_produk,
AVG(harga) AS rata_harga,
MIN(harga) AS harga_min,
MAX(harga) AS harga_max,
SUM(stok) AS total_stok
FROM produk
GROUP BY kategori
ORDER BY rata_harga DESC;
-- Pendapatan per bulan
SELECT
YEAR(tanggal_pesanan) AS tahun,
MONTH(tanggal_pesanan) AS bulan,
COUNT(*) AS jumlah_order,
SUM(total) AS pendapatan
FROM pesanan
WHERE status = 'selesai'
GROUP BY YEAR(tanggal_pesanan), MONTH(tanggal_pesanan)
ORDER BY tahun DESC, bulan DESC;
-- =============================================
-- HAVING: Filter SETELAH GROUP BY
-- =============================================
-- WHERE → filter SEBELUM grouping
-- HAVING → filter SETELAH grouping
-- Kota dengan lebih dari 5 pelanggan
SELECT
kota,
COUNT(*) AS jumlah
FROM pelanggan
GROUP BY kota
HAVING COUNT(*) > 5
ORDER BY jumlah DESC;
-- Kategori dengan rata-rata harga di atas 1 juta
SELECT
kategori,
AVG(harga) AS rata_harga,
COUNT(*) AS jumlah_produk
FROM produk
GROUP BY kategori
HAVING AVG(harga) > 1000000;
-- Pelanggan yang sudah belanja lebih dari 5 juta
SELECT
pl.nama,
COUNT(p.id_pesanan) AS jumlah_order,
SUM(p.total) AS total_belanja
FROM pelanggan pl
INNER JOIN pesanan p ON pl.id_pelanggan = p.id_pelanggan
WHERE p.status = 'selesai'
GROUP BY pl.id_pelanggan, pl.nama
HAVING SUM(p.total) > 5000000
ORDER BY total_belanja DESC;
Pahami urutan eksekusi clause SQL agar tidak bingung antara WHERE dan HAVING:
FROM / JOIN— Tentukan tabelWHERE— Filter baris sebelum groupingGROUP BY— Kelompokkan barisHAVING— Filter grup setelah groupingSELECT— Pilih kolom yang ditampilkanORDER BY— Urutkan hasilLIMIT / OFFSET— Batasi jumlah baris
8. INSERT, UPDATE, DELETE — Operasi CRUD
Selain membaca data dengan SELECT, kita perlu menambah, mengubah, dan menghapus data. Ketiga operasi ini bersama SELECT membentuk operasi CRUD.
INSERT — Menambah Data
-- =============================================
-- INSERT: Menambah data baru
-- =============================================
-- INSERT satu baris
INSERT INTO pelanggan (nama, email, telepon, kota)
VALUES ('Budi Santoso', 'budi@email.com', '08123456789', 'Jakarta');
-- INSERT beberapa baris sekaligus
INSERT INTO pelanggan (nama, email, telepon, kota) VALUES
('Ani Wijaya', 'ani@email.com', '08234567890', 'Bandung'),
('Citra Dewi', 'citra@email.com', '08345678901', 'Surabaya'),
('Deni Pratama', 'deni@email.com', NULL, 'Yogyakarta'),
('Eka Putri', 'eka@email.com', '08567890123', 'Jakarta');
-- INSERT produk
INSERT INTO produk (nama_produk, harga, stok, kategori, deskripsi) VALUES
('Laptop ASUS ROG', 15000000, 25, 'Elektronik', 'Laptop gaming high-end'),
('Keyboard Mekanik', 850000, 100, 'Aksesoris', 'Cherry MX Red switches'),
('Mouse Logitech G502', 650000, 80, 'Aksesoris', 'Gaming mouse 25K DPI'),
('Monitor 27" 4K', 4500000, 15, 'Elektronik', 'IPS panel, 144Hz'),
('Webcam Logitech C920', 1200000, 50, 'Aksesoris', 'Full HD 1080p'),
('USB Hub 7-Port', 150000, 200, 'Aksesoris', 'USB 3.0 dengan LED');
-- INSERT dari SELECT (copy data)
INSERT INTO pelanggan_backup (nama, email, kota)
SELECT nama, email, kota
FROM pelanggan
WHERE is_active = TRUE;
UPDATE — Mengubah Data
-- =============================================
-- UPDATE: Mengubah data yang sudah ada
-- =============================================
-- Update satu kolom
UPDATE pelanggan
SET kota = 'Bandung'
WHERE id_pelanggan = 1;
-- Update beberapa kolom sekaligus
UPDATE pelanggan
SET
telepon = '08111111111',
kota = 'Semarang',
is_active = TRUE
WHERE email = 'budi@email.com';
-- Update dengan kondisi kompleks
-- Naikkan harga 10% untuk produk Elektronik
UPDATE produk
SET harga = harga * 1.10
WHERE kategori = 'Elektronik';
-- Kurangi stok saat pesanan diproses
UPDATE produk
SET stok = stok - 2
WHERE id_produk = 1 AND stok >= 2;
-- Update dengan subquery
-- Set status 'vip' untuk pelanggan yang belanja > 10 juta
UPDATE pelanggan
SET kota = 'VIP'
WHERE id_pelanggan IN (
SELECT id_pelanggan
FROM pesanan
GROUP BY id_pelanggan
HAVING SUM(total) > 10000000
);
DELETE — Menghapus Data
-- =============================================
-- DELETE: Menghapus data
-- =============================================
-- Hapus satu baris
DELETE FROM pelanggan
WHERE id_pelanggan = 5;
-- Hapus dengan kondisi
-- Hapus semua pesanan yang dibatalkan
DELETE FROM pesanan
WHERE status = 'batal';
-- Hapus data lama (lebih dari 1 tahun)
DELETE FROM pesanan
WHERE tanggal_pesanan < DATE_SUB(NOW(), INTERVAL 1 YEAR);
-- Hapus dengan subquery
-- Hapus produk yang tidak pernah dipesan
DELETE FROM produk
WHERE id_produk NOT IN (
SELECT DISTINCT id_produk
FROM detail_pesanan
);
-- ⚠️ HATI-HATI: Tanpa WHERE = hapus SEMUA data!
-- DELETE FROM pelanggan; → Hapus semua pelanggan!
-- Lebih aman: TRUNCATE untuk hapus semua data (tidak bisa di-rollback)
-- TRUNCATE TABLE pelanggan;
Perintah UPDATE dan DELETE tanpa clause WHERE akan mempengaruhi SEMUA BARIS di tabel! Ini adalah kesalahan paling umum dan berbahaya dalam SQL. Sebelum menjalankan UPDATE/DELETE, jalankan SELECT dengan WHERE yang sama untuk memverifikasi baris yang akan terpengaruh.
9. Indexes — Mempercepat Query
Index adalah struktur data tambahan yang mempercepat proses pencarian data di tabel. Tanpa index, database harus memindai seluruh tabel (full table scan) — dengan index, database bisa langsung melompat ke baris yang dicari.
Analogi Index
Tanpa index seperti membaca buku telepon dari halaman pertama untuk mencari nama "Wijaya". Dengan index seperti menggunakan daftar isi — langsung tahu halaman berapa "Wijaya" berada. Semakin besar tabel, semakin besar perbedaan performanya.
-- ============================================= -- Membuat Index -- ============================================= -- Index sederhana (boleh ada duplikat) -- Berguna untuk kolom yang sering di-WHERE atau di-JOIN CREATE INDEX idx_pelanggan_kota ON pelanggan(kota); CREATE INDEX idx_produk_kategori ON produk(kategori); CREATE INDEX idx_pesanan_tanggal ON pesanan(tanggal_pesanan); -- Index unik (otomatis dibuat oleh UNIQUE constraint) -- CREATE UNIQUE INDEX idx_pelanggan_email ON pelanggan(email); -- Composite index (beberapa kolom) -- Urutan kolom penting! Index ini cocok untuk: -- WHERE kategori = 'X' AND harga > 100 -- WHERE kategori = 'X' (kolom pertama saja) -- TIDAK cocok untuk: -- WHERE harga > 100 (kolom kedua saja, tanpa kolom pertama) CREATE INDEX idx_produk_kategori_harga ON produk(kategori, harga); -- Index untuk query yang sering ORDER BY CREATE INDEX idx_produk_harga_nama ON produk(harga, nama_produk); -- ============================================= -- Melihat Index yang Sudah Ada -- ============================================= -- MySQL SHOW INDEX FROM produk; -- PostgreSQL SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'produk'; -- SQLite .indices produk -- ============================================= -- Menghapus Index -- ============================================= DROP INDEX idx_pelanggan_kota; -- MySQL/SQLite DROP INDEX idx_pelanggan_kota; -- PostgreSQL (sama) -- ============================================= -- EXPLAIN: Melihat Rencana Eksekusi Query -- ============================================= EXPLAIN SELECT * FROM produk WHERE kategori = 'Elektronik'; -- Cek kolom "type": -- ALL = full table scan (BURUK — perlu index) -- ref = menggunakan index (BAGUS) -- range = scan range dari index (CUKUP BAGUS) EXPLAIN SELECT * FROM produk WHERE kategori = 'Elektronik' AND harga > 1000000;
Kapan Menggunakan Index?
| Situasi | Rekomendasi |
|---|---|
| Kolom sering di WHERE | ✅ Buat index |
| Kolom sering di JOIN (foreign key) | ✅ Buat index |
| Kolom sering di ORDER BY | ✅ Buat index |
| Kolom punya banyak nilai unik (high cardinality) | ✅ Buat index |
| Kolom jarang di-query | ❌ Tidak perlu index |
| Tabel sangat kecil (<1000 baris) | ❌ Tidak perlu index |
| Kolom sering di-INSERT/UPDATE | ⚠️ Hati-hati — index memperlambat write |
Index mempercepat SELECT tetapi memperlambat INSERT, UPDATE, dan DELETE karena database harus memperbarui index setiap kali data berubah. Jangan buat index terlalu banyak — cukup pada kolom yang benar-benar sering digunakan untuk pencarian. Gunakan EXPLAIN untuk menganalisis query Anda.
10. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang SQL: