Database

SQL Dasar: Panduan Lengkap untuk Pemula

Pelajari SQL dari nol — SELECT, WHERE, JOIN, GROUP BY, ORDER BY, INSERT, UPDATE, DELETE, CREATE TABLE, dan indexes dengan contoh praktis

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
DatabaseKumpulan tabel yang saling berhubungandatabase toko_online
TabelStruktur data baris-kolomtabel pelanggan
Baris (Row)Satu record dataSatu pelanggan spesifik
Kolom (Column)Atribut/field dari datanama, email, telepon
Primary KeyID unik untuk setiap barisid_pelanggan
Foreign KeyReferensi ke tabel lainid_produk di tabel pesanan

Jenis Perintah SQL

Kategori Perintah Fungsi
DDL (Data Definition)CREATE, ALTER, DROPMembuat/mengubah/menghapus struktur tabel
DML (Data Manipulation)SELECT, INSERT, UPDATE, DELETEMembaca/menambah/mengubah/menghapus data
DCL (Data Control)GRANT, REVOKEMengatur hak akses pengguna
TCL (Transaction)COMMIT, ROLLBACKMengatur transaksi database
Diagram: Database Relasional
┌─────────────────────────────────────────────────────────────────┐
│                   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

Bash — Instalasi Database
# ===== 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).

SQL — CREATE TABLE
-- =============================================
-- 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 / INTEGERBilangan bulat42, -1, 0
DECIMAL(p,s)Bilangan desimal dengan presisi15000000.50
VARCHAR(n)Teks dengan panjang maksimum n'Budi Santoso'
TEXTTeks panjang tanpa batas'Lorem ipsum dolor sit amet...'
DATETanggal (tanpa waktu)'2026-06-25'
DATETIME / TIMESTAMPTanggal dan waktu'2026-06-25 14:30:00'
BOOLEANTrue / FalseTRUE, FALSE
ENUM('a','b')Pilihan nilai tetap'pending', 'selesai'

Constraint Umum

Constraint Fungsi
PRIMARY KEYIdentifikasi unik setiap baris — tidak boleh NULL dan tidak boleh duplikat
FOREIGN KEYRelasi ke tabel lain — menjaga integritas referensial
NOT NULLKolom wajib diisi — tidak boleh kosong
UNIQUENilai harus unik — tidak boleh ada duplikat
DEFAULTNilai default jika tidak diisi
CHECKValidasi kondisi tertentu (misal: harga > 0)
AUTO_INCREMENTOtomatis 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).

SQL — SELECT Dasar
-- =============================================
-- 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;
💡 Hindari SELECT *

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.

SQL — WHERE Clause
-- =============================================
-- 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 dengankota = 'Jakarta'
!= / <>Tidak sama denganstatus != 'batal'
>, <, >=, <=Perbandinganharga >= 1000000
BETWEENRentang nilai (inklusif)harga BETWEEN 100 AND 500
INDalam daftar nilaikota IN ('JKT', 'BDG')
LIKEPencarian polanama LIKE 'B%'
IS NULLCek nilai kosongtelepon IS NULL
AND, OR, NOTOperator logikaA 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).

SQL — ORDER BY
-- =============================================
-- 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.

Diagram: Jenis JOIN
   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)
SQL — JOIN
-- =============================================
-- 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 JOINHanya baris yang cocok di kedua tabelData yang pasti punya relasi
LEFT JOINSemua baris kiri + yang cocok dari kananIngin tampilkan semua data utama
RIGHT JOINSemua baris kanan + yang cocok dari kiriJarang dipakai — bisa ditulis sebagai LEFT JOIN
FULL JOINSemua baris dari kedua tabelIngin semua data dari kedua sisi
CROSS JOINKombinasi 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().

SQL — GROUP BY & Aggregate Functions
-- =============================================
-- 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;
📖 Urutan Eksekusi SQL

Pahami urutan eksekusi clause SQL agar tidak bingung antara WHERE dan HAVING:

  1. FROM / JOIN — Tentukan tabel
  2. WHERE — Filter baris sebelum grouping
  3. GROUP BY — Kelompokkan baris
  4. HAVING — Filter grup setelah grouping
  5. SELECT — Pilih kolom yang ditampilkan
  6. ORDER BY — Urutkan hasil
  7. LIMIT / 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

SQL — INSERT
-- =============================================
-- 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

SQL — UPDATE
-- =============================================
-- 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

SQL — DELETE
-- =============================================
-- 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;
🚫 SELALU GUNAKAN WHERE!

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

📖 Bayangkan Buku Telepon

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.

SQL — Indexes
-- =============================================
-- 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
⚠️ Trade-off Index

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:

Pertanyaan 1: Clause SQL apa yang digunakan untuk memfilter baris setelah GROUP BY?

a) WHERE
b) HAVING
c) FILTER
d) ORDER BY

Pertanyaan 2: Jenis JOIN yang mengembalikan semua baris dari tabel kiri dan hanya yang cocok dari tabel kanan adalah?

a) INNER JOIN
b) RIGHT JOIN
c) LEFT JOIN
d) CROSS JOIN

Pertanyaan 3: Apa yang terjadi jika menjalankan DELETE FROM pelanggan; tanpa clause WHERE?

a) Menghapus baris pertama saja
b) Error karena tidak ada WHERE
c) Menghapus SEMUA baris dari tabel pelanggan
d) Menghapus tabel pelanggan beserta strukturnya

Pertanyaan 4: Fungsi dari INDEX pada tabel database adalah?

a) Mengenkripsi data untuk keamanan
b) Mempercepat proses pencarian dan query SELECT
c) Mengurangi ukuran penyimpanan database
d) Membuat backup otomatis dari tabel

Pertanyaan 5: Urutan eksekusi SQL yang benar dari clause berikut adalah?

a) SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
b) FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
c) FROM → SELECT → WHERE → GROUP BY → ORDER BY → HAVING
d) WHERE → FROM → GROUP BY → SELECT → HAVING → ORDER BY
🔍 Zoom
100%
🎨 Tema