Data yang sudah tersimpan baru berharga ketika bisa dibaca kembali dengan cepat dan tepat — dan SELECT adalah statement yang paling sering kita ketik sepanjang karier bekerja dengan database. Statement ini tampak sederhana, tetapi lengkap dengan ekspresi, alias, pengurutan, pembatasan jumlah baris, hingga penghapusan duplikat. Chapter ini membedah semuanya menggunakan data toko yang sudah kita isi pada Chapter Memasukkan Data dengan INSERT.

Data latihan pada chapter ini berada di toko.db hasil sesi sebelumnya. Jika kita memulai dari titik ini, siapkan dulu tabel beserta isinya:

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

CREATE TABLE produk (
    id INTEGER PRIMARY KEY,
    nama TEXT NOT NULL UNIQUE,
    harga INTEGER NOT NULL CHECK (harga >= 0),
    stok INTEGER DEFAULT 0
);

INSERT INTO pelanggan (nama, kota) VALUES
    ('Budi', 'Jakarta'),
    ('Siti', 'Bandung'),
    ('Ani', 'Surabaya'),
    ('Joko', 'Jakarta'),
    ('Rina', NULL);

INSERT INTO produk (nama, harga, stok) VALUES
    ('Pensil', 3000, 120),
    ('Buku Tulis', 5500, 45),
    ('Penghapus', 2500, 80),
    ('Tas Sekolah', 85000, 12),
    ('Botol Air', 25000, 30),
    ('Rautan Pensil', 8000, NULL);

INSERT INTO produk (nama, harga) VALUES ('Papan Tulis Kecil', 15000);

Memilih Kolom #

Asterisk * berarti “semua kolom” — praktis untuk mengintip isi tabel:

text
sqlite> SELECT * FROM pelanggan;
1|Budi|Jakarta
2|Siti|Bandung
3|Ani|Surabaya
4|Joko|Jakarta
5|Rina|
sqlite>

Untuk kebutuhan nyata, sebutkan kolom yang benar-benar diperlukan. Hasilnya lebih ramping, dan pembaca query langsung tahu apa yang akan didapat:

text
sqlite> SELECT nama, harga FROM produk;
Pensil|3000
Buku Tulis|5500
Penghapus|2500
Tas Sekolah|85000
Botol Air|25000
Rautan Pensil|8000
Papan Tulis Kecil|15000
sqlite>

Tanpa ORDER BY, urutan baris hasil tidak dijamin — pada tabel kecil seperti ini SQLite mengembalikan baris dalam urutan rowid, tetapi engine berhak mengubah strategi. Kebiasaan amannya: tulis ORDER BY setiap kali urutan berarti.

Ekspresi dan Alias #

Daftar SELECT tidak terbatas pada nama kolom — kita bisa menulis ekspresi yang menghitung nilai baru. AS memberi nama pada kolom hasil:

text
sqlite> SELECT nama, harga * stok AS nilai_stok
   ...> FROM produk
   ...> ORDER BY nilai_stok DESC
   ...> LIMIT 3;
Tas Sekolah|1020000
Botol Air|750000
Pensil|360000
sqlite>

Ekspresi harga * stok menghitung nilai barang yang berada di rak untuk tiap baris produk. Alias nilai_stok membuat kolom hasil bernama jelas, sekaligus bisa dirujuk oleh ORDER BY di baris berikutnya.

Satu detail penting terlihat di sini: Rautan Pensil tidak muncul di tiga besar. Kolom stok-nya NULL, dan harga * NULL menghasilkan NULL — bukan nol. Baris dengan nilai NULL ini juga berpengaruh saat pengurutan, seperti berikutnya.

Mengurutkan dengan ORDER BY #

ORDER BY menerima satu atau lebih kolom, naik (ASC, bawaan) atau turun (DESC). Tabel pelanggan di bawah diurutkan berdasarkan dua kolom: kota dahulu, lalu nama:

text
sqlite> SELECT kota, nama FROM pelanggan ORDER BY kota, nama;
|Rina
Bandung|Siti
Jakarta|Budi
Jakarta|Joko
Surabaya|Ani
sqlite>

Urutan dua kolom berarti kolom kedua hanya dipakai untuk memecah seri pada kolom pertama: Budi dan Joko sama-sama dari Jakarta, maka keduanya diurutkan lagi berdasarkan nama. Perhatikan juga Rina muncul paling atas — di SQLite, NULL dianggap lebih kecil dari nilai apa pun, jadi NULL selalu muncul pertama pada urutan naik dan terakhir pada urutan turun.

SQLite juga menyediakan kendali eksplisit sejak versi 3.30: NULLS FIRST dan NULLS LAST:

text
sqlite> SELECT nama, stok FROM produk ORDER BY stok NULLS LAST;
Papan Tulis Kecil|0
Tas Sekolah|12
Botol Air|30
Buku Tulis|45
Penghapus|80
Pensil|120
Rautan Pensil|
sqlite>

Dengan NULLS LAST, baris Rautan Pensil yang berstok NULL turun ke baris paling bawah meski urutannya naik.

Membatasi Hasil dengan LIMIT dan OFFSET #

LIMIT membatasi jumlah baris, OFFSET melewati sejumlah baris pertama. Keduanya sering dipakai untuk pagination:

text
sqlite> SELECT nama, harga FROM produk ORDER BY harga DESC LIMIT 3;
Tas Sekolah|85000
Botol Air|25000
Papan Tulis Kecil|15000
sqlite> SELECT nama, harga FROM produk ORDER BY harga DESC LIMIT 2 OFFSET 3;
Rautan Pensil|8000
Buku Tulis|5500
sqlite>

Query pertama mengambil tiga termahal. Query kedua melewati tiga baris teratas lalu mengambil dua baris berikutnya — seperti membuka halaman dua dari daftar produk termahal. SQLite juga menerima bentuk singkat LIMIT 2 OFFSET 3 menjadi LIMIT 3, 2 (offset ditulis lebih dulu), tetapi bentuk panjang jauh lebih mudah dibaca.

Menghilangkan Duplikat dengan DISTINCT #

DISTINCT melipat baris-baris hasil yang identik. Tabel pelanggan kita memuat dua penduduk Jakarta, tetapi query berikut menampilkan tiap kota hanya sekali:

text
sqlite> SELECT DISTINCT kota FROM pelanggan ORDER BY kota;

Bandung
Jakarta
Surabaya
sqlite>

Tabel memuat lima nilai kota — Jakarta dua kali, ditambah satu NULL — tetapi hasilnya hanya empat baris: NULL milik Rina muncul sebagai baris kosong paling atas (teks kosong dalam mode list, dan NULL dianggap terkecil dalam urutan naik), disusul tiga kota yang masing-masing tampil sekali.

SELECT seperti ini membaca seluruh isi tabel. Sering kali kita hanya butuh sebagian baris — dan itulah tugas WHERE, statement pembahasan Chapter Memfilter Data dengan WHERE.