Anda di halaman 1dari 361

EKSKLUSIF

KILLER TRIK
QUERY MySQL
Kupas Tuntas Teknik Olah
Data Dengan Query MySQL

SQL
Agus Prawoto Hadi
Killer Trik Query MySQL i
Killer Trik Query MySQL – Kupas
Tuntas Teknik Olah Data Dengan
Query MySQL
Penulis : Agus Prawoto Hadi

Edisi : I (Pertama)

Terbit : Juli 2017

Dimensi : 14,5 cm x 21 cm

Jumlah Halaman: xiv + 347

Hak cipta ada di penulis, dilarang menyalin dan


mempublikasikan sebagian maupun keseluruhan isi dari buku
ini tanpa ijin penulis
Kata Pengantar
Alhamdulillah, penulis panjatkan kehadirat Allah S.W.T,
karena dengan rahmat dan hidayah-Nya penulis dapat
menyelesaikan buku "Killer Trik Query MySQL – Mengungkap
Rahasia Master SQL.".

Ketika bekerja dengan aplikasi, kita tidak bisa lepas dari


penyajian data dari database. Bentuk penyajian ini beragam, mulai
dari yang paling sederhana hingga yang kompleks sesuai dengan
informasi yang ingin disajikan. Hal ini, bagi sebagian orang sulit
dilakukan karena minimnya referensi yang tersedia.

Buku ini akan memberikan pemahaman kepada Anda


bagaimana menyusun query dengan benar sehingga ketika
menghadapi berbagai permasalahan terkait penyajian data, Anda
akan langsung tahu bagaimana cara menyelesaikannya

Penulis menyadari bahwa buku ini masih jauh dari sempurna,


untuk itu penulis menerima segala kritik dan saran yang
membangun untuk perbaikan di versi berikutnya. Kritik dan saran
dapat dikirim ke alamat email penulis di prawoto.hadi@gmail.com

Akhir kata semoga buku ini dapat membawa manfaat bagi


pembaca.

Kudus, Juli 2017

Penulis

Killer Trik Query MySQL iii


Istilah Penting
Statemen, Klausa, dan Keyword
Pada buku ini, Anda akan sering menemui beberapa istilah terkait
dengan query SQL diantaranya adalah: Statemen, Klausa, dan
terkadang keyword, apa beda ketiganya?

Statemen
Statemen adalah sebuah Query SQL utuh yang dapat dieksekusi
dengan baik.

Sebuah statemen biasanya diakhiri dengan tanda titik koma (;).


Tanda ini disebut delimiter.

Jika query hanya terdiri dari satu statemen, penggunaan delimiter ini
bersifat opsional, sedangkan jika query terdiri dari lebih dari satu
statemen, delimiter ini harus digunakan.

Umumnya Query terdiri hanya satu statemen, namun pada kondisi


tertentu, Query dapat terdiri dari lebih dari satu statemen.

Contoh statemen: statemen SELECT, statemen INSERT, dll

Klausa
Klausa merupakan bagian bagian tertentu dari statemen, sehingga
cakupnnya nya lebih sempit, misal klausa FROM, klausa SELECT, dst

Keyword
Keyword merupakan kata yang digunakan oleh MySQL yang
mewakili fungsi tertentu, misal COUNT, SELECT, SUM, dll. Dalam
buku ini, terkadang kita menyebut keyword dengan klausa.

iv Istilah Penting
Perbedaan ketiga istilah diatas dapat dilihat pada gambar berikut ini:

Gambar: Ilustrasi Statemen, Klausa, dan Keyword

Dengan memahami istilah tersebut diatas, Anda akan lebih mudah


untuk memahami pembahasan yang ada di dalam buku ini

Killer Trik Query MySQL v


Coding Style
Ketika menulis syntax SQL, kita bebas menggunakan gaya masing
masing, tidak ada standar pola penulisan yang baku, namun
demikian, agar syntax mudah dibaca dan dipahami, maka sangat
disarankan untuk menuliskannya dengan rapi.

Agar syntax SQL terlihat rapi, gunakan spasi, tab, dan enter
secukupnya, sehingga dengan mudah dibedakan mana statemen,
klausa, dan keyword.

Didalam query SQL, kita bebas menggunakan spasi dan line break
dimana saja, karena ketika query tersebut dieksekusi, spasi dan line
break (enter/baris baru) akan diabaikan.

Contoh penulisan syntax yang baik:

1. SELECT b.kd_barang,
2. b.nama_barang,
3. SUM(p.total_trx) AS total_pnjualan
4. FROM barang AS b
5. LEFT JOIN penjualan_detail AS pd USING(kd_barang)
6. LEFT JOIN penjualan AS p USING(id_trx)
7. WHERE YEAR(tgl_trx) = 2017
8. GROUP BY kd_barang

Pada contoh query diatas, perhatikan penggunaan tab, spasi , dan


line break. Perhatikan juga penggunaan tab pada klausa selain
keyword utama (SELECT, FROM, WHERE, GROUP BY, ORDER BY,
LIMIT, dan HAVING)

Khusus penulisan nama kolom pada klausa SELECT, jika nama kolom
dipisahkan dengan baris, maka ada dua cara punisan koma, yaitu (1)
koma dibelakang nama kolom, seperti pada contoh diatas, dan (2)
koma di depan nama kolom seperti contoh berikut:

vi Coding Style
1. SELECT b.kd_barang
2. , b.nama_barang
3. , SUM(p.total_trx) AS total_pnjualan
4. FROM ...

Kedua cara diatas sering digunakan oleh para pengembang SQL,


cara pertama telihat lebih rapi, namun ketika ingin menghilangkan
baris terakhir, kita juga harus menghapus koma diatasnya, karena
sebelum klausa FROM tidak boleh ada koma, sedangkan cara kedua,
kita hanya perlu menghapus kolom yang ingin dihilangkan, tidak
perlu khawatir kelebihan koma. Perhatikan ilustrasi berikut:

Gambar: Ilustrasi gaya penulisan klausa SELECT

Agar familiar dengan keduanya, pada buku ini kita akan


menggunakan keduanya secara bergantian.

Killer Trik Query MySQL vii


Source Code
Buku ini disertai file dump database yaitu tutorial_trik.sql, silakan
load file tersebut menggunakan aplikasi database manager seperti
HeidiSQL atau phpMyAdmin. Setelah di load, akan terbentuk
database tutorial_trik, di sepanjang buku ini, kita akan sering
menggunakan database tersebut untuk latihan.

Selain file sql, juga disertakan file .php. File ini merupakan source
code script PHP yang digunakan pada pembahasan BAB 15 Table
Reporting Dengan PHP dan MySQL:. Untuk menggunakannya,
silakan letakkan file di dalam folder htdocs kemudian akses file
tersebut melalui browser. File php tersebut menggunakan
konfigurasi koneksi server: localhost, user: root, password: '', jika
konfigurasi server MySQL yang Anda gunakan berbeda, edit tiap tiap
file .php sebelum dijalankan

viii Source Code


Daftar Isi
Kata Pengantar ............................................................................................................. iii

Istilah Penting ................................................................................................................ iv

Coding Style..................................................................................................................... vi

Source Code.................................................................................................................. viii

Daftar Isi ........................................................................................................................... ix

BAB 1 Statemen SELECT............................................................................................. 1

1.1. Syntax Statemen SELECT .............................................................................. 1

1.2. Operator Aritmetika........................................................................................ 9

1.3. Operator Perbandingan ............................................................................. 12

1.4. Nilai NULL ....................................................................................................... 12

1.5. Operator OR dan AND ................................................................................. 14

BAB 2 Kunci Menguasai SELECT .......................................................................... 21

2.1. Memahami Urutan Eksekusi..................................................................... 21

2.2. Konsekuensi Urutan Eksekusi ................................................................. 33

2.3. Identifikasikan Bentuk Tabel ................................................................... 39

2.4. Pendefinisian Kolom Baru ......................................................................... 43

2.5. Eksekusi Baris................................................................................................. 45

2.6. Pembatas Tampilan Data ........................................................................... 46

2.7. Poin Penting..................................................................................................... 46

BAB 3 Ekspresi Logika.............................................................................................. 49

3.1. Ekspresi IF ........................................................................................................ 49

Killer Trik Query MySQL ix


3.2. Ekspresi CASE ................................................................................................. 52

BAB 4 Menguasai JOIN.............................................................................................. 57

4.1. JOIN...................................................................................................................... 58

4.2. USING atau ON ................................................................................................ 59

4.3. LEFT JOIN.......................................................................................................... 60

4.4. RIGHT JOIN ....................................................................................................... 61

4.5. Hal Penting Tentang JOIN .......................................................................... 63

BAB 5 Menguasai Fungsi Agregasi ...................................................................... 67

5.1. Memahami Cara Kerja Fungsi Agragasi ............................................... 67

5.2. MAX, MIN, dan AVG....................................................................................... 70

5.3. COUNT dan SUM ............................................................................................ 73

5.4. SUM Dengan IF ............................................................................................... 75

5.5. COUNT Dengan CASE ................................................................................... 80

5.6. Penggabungan Fungsi Agregasi Dengan Ekspresi Logika .... 84

5.7. Studi Kasus: COUNT IF ................................................................................ 90

BAB 6. Menguasai Fungsi Scalar ........................................................................ 103

6.1. Memahami Cara Kerja Fungsi Scalar...................................................103

6.2. Fungsi String..................................................................................................105

6.3. Fungsi Date Time .........................................................................................108

6.4. Fungsi Beda Waktu .....................................................................................115

6.5. Fungsi Numeric ............................................................................................118

BAB 7 Pendalaman Materi: Fungsi Agregasi, Skalar, Logika ................. 123

x Daftar Isi
7.1. Fungsi Scalar Pada WHERE..................................................................... 123

7.2. Fungsi Scalar Pada GROUP BY ............................................................... 125

7.3. Studi Kasus ..................................................................................................... 127

BAB 8 Mempertajam Agregasi dan JOIN ........................................................ 133

8.1. Studi Kasus #1 .............................................................................................. 133

8.2. Studi Kasus #2 .............................................................................................. 137

8.3. Studi Kasus #3 .............................................................................................. 142

8.4. Studi Kasus #4 .............................................................................................. 147

8.5. Studi Kasus #5 .............................................................................................. 152

BAB 9 Menguasai SUBQUERY............................................................................ 159

9.1. Subquery Sebagai Kolom ......................................................................... 159

9.2 Subquery Sebagai Data .............................................................................. 162

BAB 10 Menguasai UNION ................................................................................... 171

10.1. Memahami UNION dan UNION ALL .................................................. 172

10.2. Studi Kasus #1 ........................................................................................... 184

10.3. Studi Kasus #2 ........................................................................................... 190

10.4. Studi Kasus #3 ........................................................................................... 194

BAB 11 Menguasai WITH ROLLUP ................................................................... 197

11.1. Memahami Cara Kerja WITH ROLLUP............................................. 197

11.2. Mengganti Nilai NULL ............................................................................. 201

11.3. ORDER BY Pada WITH ROLLUP ......................................................... 203

11.4. Studi kasus................................................................................................... 208

Killer Trik Query MySQL xi


BAB 12. Menguasai Variable ............................................................................... 211

12.1. Memahami Variable .................................................................................211

12.2. Single Statement........................................................................................213

12.3. Studi Kasus #1............................................................................................216

12.4. Studi Kasus #2............................................................................................217

BAB 13 Menguasai DATE TIME ......................................................................... 221

13.1. Studi Kasus #1............................................................................................221

13.2. Studi Kasus #2............................................................................................225

13.3. Studi Kasus #3............................................................................................227

13.4. Studi Kasus #4............................................................................................232

13.5. Studi Kasus #5............................................................................................239

BAB 14 Table Reporting ....................................................................................... 243

14.1. Table Report I .............................................................................................243

14.2. Table Report II ...........................................................................................252

14.3. Table Report III..........................................................................................262

14.4. Keterbatasan ...............................................................................................272

BAB 15 Table Reporting Dengan PHP dan MySQL .................................... 275

15.1. Query MySQL Dengan PHP ...................................................................275

15.2. Table Reporting I ......................................................................................279

15.3. Table Reporting II .....................................................................................295

15.3. Table Reporting III ...................................................................................306

15.4. Table Reporting IV ...................................................................................321

xii Daftar Isi


15.5. Lebih Lanjut…............................................................................................. 346

Next Step ..................................................................................................................... 347

Killer Trik Query MySQL xiii


Halaman ini sengaja dikosongkan
Jagowebdev.com

xiv Daftar Isi


BAB 1 Statemen SELECT
Statemen SELECT digunakan untuk mengambil data dari database.
Statemen ini merupakan statemen utama SQL karena sebagian
besar query yang kita lakukan adalah pengambilan data, disamping
itu statemen ini memiliki variasi paling banyak dibanding statemen
lain seperti INSERT dan UPDATE.

Pada BAB ini kita akan membahas syntax dasar statemen SELECT
beserta variasinya, dengan memahami topik ini, Anda akan dapat
dengan mudah memahami pembahasan pada bab bab berikutnya.

1.1. Syntax Statemen SELECT


Statemen SELECT memiliki struktur sebagai berikut:

SELECT ...
FROM ...
[WHERE ...]
[GROUP BY ...]
[ORDER BY ...]
[HAVING ...]
[LIMIT ...]

Penulisan klausa diatas harus urut mulai dari atas ke bawah.


Keyword SELECT dan FROM wajib ada, sedangkan keyword lain (yang
ada didalam tanda kurung siku [] ) bersifat opsional.

Contoh penggunaan statemen SELECT: misal kita memiliki tabel mhs


dengan data sebagai berikut:
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |

Killer Trik Query MySQL 1


| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
| 008 | Haris | L | NULL |
+------+---------+---------------+------------+

Untuk mengambil semua kolom, kita gunakan tanda asterik (*) misal:

1. SELECT * FROM mhs

Hasil:
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |
| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
| 008 | Haris | L | NULL |
+------+---------+---------------+------------+

Untuk menampilkan kolom tertentu, kita tulis nama kolom tersebut


pada klausa SELECT, misal:

1. SELECT nama, jenis_kelamin FROM mhs;

Hasil
+---------+---------------+
| nama | jenis_kelamin |
+---------+---------------+
| Alfa | L |
| Beta | P |
| Charlie | P |
| Delta | L |
| Erdhi | L |
| Farah | P |
| Gisel | P |

2 BAB 1 Statemen SELECT


| Haris | L |
+---------+---------------+

Usahakan hanya mengambil kolom yang diperlukan, hindari


penggunaan asterik. Hal ini merupakan "Best Practice" untuk
meningkatkan efisiensi performa sql.

WHERE

Klausa WHERE digunakan untuk memfilter data yang ingin diambil,


misal kita akan mengambil data siswa dengan jenis kelamin laki-laki,
query yang kita jalankan adalah sebagai berikut:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. WHERE jenis_kelamin = "L"

Hasil:
+-------+---------------+
| nama | jenis_kelamin |
+-------+---------------+
| Alfa | L |
| Delta | L |
| Erdhi | L |
| Haris | L |
+-------+---------------+

Ketika menjalankan klausa WHERE, MySQL akan memeriksa baris


pada tabel satu persatu sehingga pada tabel dengan jumlah baris
banyak, proses ini akan memakan waktu cukup lama, untuk
mengatasinya, gunakan teknik indexing (penggunaan index) - tidak
di bahas pada buku ini-.

GROUP BY

GROUP BY digunakan untuk mengelompokkan baris berdasarkan


kolom tertentu.

Killer Trik Query MySQL 3


Penggunaan keyword ini selalu bersamaan dengan fungsi agregsi
seperti COUNT, SUM, MIN, MAX, dan AVG

Contoh:

1. SELECT kd_jurusan, COUNT(kd_jurusan)


2. FROM mhs
3. WHERE jenis_kelamin = "L"
4. GROUP BY kd_jurusan

Pada contoh diatas, data dihitung (fungsi COUNT) dan


dikelompokkan berdasarkan kolom kd_jurusan. Jika Anda belum
paham mengenai fungsi COUNT, tidak usah khawatir, kita akan
membahasnya secara detail pada bab berikutnya.

Penting diperhatikan bahwa sebelum menjalankan GROUP BY,


terlebih dahulu MySQL akan mengurutkan data pada kolom yang
ada pada klausa GROUP BY secara ascending, (hal ini penting untuk
diingat)

Sebagai contoh, jika kita jalankan query diatas, maka hasil yang kita
peroleh adalah:
+------------+-------------------+
| kd_jurusan | COUNT(kd_jurusan) |
+------------+-------------------+
| NULL | 0 |
| J001 | 2 |
| J002 | 1 |
+------------+-------------------+

Pada tabel diatas terlihat bahwa data diurutkan berdasarkan kolom


kd_jurusan secara ascending

4 BAB 1 Statemen SELECT


ORDER BY

Keyword ORDER BY digunakan untuk mengurutkan data baik secara


ascending (dari kecil ke besar) maupun descending (dari besar ke
kecil)

Keyword ini akan dijalankan setelah MySQL menjalankan klausa


GROUP BY (jika ada) yang artinya setelah data dikelompokkan,
contoh:

1. SELECT kd_jurusan, COUNT(kd_jurusan)


2. FROM mhs
3. WHERE jenis_kelamin = "L"
4. GROUP BY kd_jurusan
5. ORDER BY kd_jurusan DESC

Hasil:
+------------+-------------------+
| kd_jurusan | COUNT(kd_jurusan) |
+------------+-------------------+
| J002 | 1 |
| J001 | 2 |
| NULL | 0 |
+------------+-------------------+

Pada contoh diatas, data diurutkan berdasarkan kolom kd_jurusan


secara descending (dari besar ke kecil). Jika ingin diurutkan secara
ascending, hilangkan keyword DESC atau ganti dengan ASC

Selain bersama dengan GROUP BY, keyword ORDER BY juga dapat


digunakan tanpa GROUP BY, misal:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. ORDER BY nama

Killer Trik Query MySQL 5


Hasil:
+---------+---------------+
| nama | jenis_kelamin |
+---------+---------------+
| Alfa | L |
| Beta | P |
| Charlie | P |
| Delta | L |
| Erdhi | L |
| Farah | P |
| Gisel | P |
| Haris | L |
+---------+---------------+

Pada contoh diatas, data diurutkan berdasarkan kolom nama secara


ascending.

Dengan ORDER BY, kita dapat mengurutkan data dengan kriteria


lebih dari satu kolom, misal:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. ORDER BY jenis_kelamin, nama

Hasil:
+---------+---------------+
| nama | jenis_kelamin |
+---------+---------------+
| Alfa | L |
| Delta | L |
| Erdhi | L |
| Haris | L |
| Beta | P |
| Charlie | P |
| Farah | P |
| Gisel | P |
+---------+---------------+

Pada contoh diatas, data diurutkan secara ascending berdasarkan


kolom jenis_kelamin, selanjutnya pada jenis kelamin yang sama,
data pada kolom nama diurutkan secara ascending.

6 BAB 1 Statemen SELECT


Untuk mengubah menjadi descending, kita dapat menambahkan
keyword DESC pada masing masing kolom, misal:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. ORDER BY jenis_kelamin DESC, nama DESC

Hasil:
+---------+---------------+
| nama | jenis_kelamin |
+---------+---------------+
| Gisel | P |
| Farah | P |
| Charlie | P |
| Beta | P |
| Haris | L |
| Erdhi | L |
| Delta | L |
| Alfa | L |
+---------+---------------+

HAVING

Keyword having sama seperti keyword WHERE, bedanya HAVING


dapat dijalankan setelah klausa GROUP BY sedangkan WHERE tidak,
contoh:

1. SELECT kd_jurusan, COUNT(kd_jurusan)


2. FROM mhs
3. WHERE jenis_kelamin = "L"
4. GROUP BY kd_jurusan
5. HAVING kd_jurusan = "J002"

Hasil:
+------------+-------------------+
| kd_jurusan | COUNT(kd_jurusan) |
+------------+-------------------+
| J002 | 1 |
+------------+-------------------+

Killer Trik Query MySQL 7


Having dijalankan setelah tabel diolah sehingga kita dapat
menggunakannya untuk memfilter data hasil olahan, misal:

1. SELECT kd_jurusan, COUNT(kd_jurusan) AS jml_mhs


2. FROM mhs
3. WHERE jenis_kelamin = "L"
4. GROUP BY kd_jurusan
5. HAVING jml_mhs > 0

Hasil:
+------------+-------------------+
| kd_jurusan | COUNT(kd_jurusan) |
+------------+-------------------+
| J001 | 2 |
| J002 | 1 |
+------------+-------------------+

LIMIT

Keyword LIMIT digunakan untuk membatasi jumlah baris yang ingin


ditampilkan, misal:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. LIMIT 5;

Hasil:
+---------+---------------+
| nama | jenis_kelamin |
+---------+---------------+
| Alfa | L |
| Beta | P |
| Charlie | P |
| Delta | L |
| Erdhi | L |
+---------+---------------+

Pada contoh diatas, data diambil 5 teratas. Secara default LIMIT akan
menghitung data mulai dari baris pertama, kita dapat menentukan

8 BAB 1 Statemen SELECT


mulai baris keberapa data diambil dengan menambahkan
parameter offset, misal:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. LIMIT 3, 5;

Hasil:
+-------+---------------+
| nama | jenis_kelamin |
+-------+---------------+
| Delta | L |
| Erdhi | L |
| Farah | P |
| Gisel | P |
| Haris | L |
+-------+---------------+

Pada contoh query diatas, nilai offsetnya adalah 3

Penghitungan offset dimulai dari 0, sehingga, pada contoh diatas,


karena kita mengambil sebanyak 5 data dan dimulai dari offset ke 3
(LIMIT 3, 5) maka data diambil mulai data ke-4, jika ingin mengambil
mulai dari data pertama, gunakan offset 0 (LIMIT 0, 5) ataucukup
ditulis LIMIT 5

1.2. Operator Aritmetika


Kolom pada klausa SELECT dapat diisi berbagai nilai termasuk
ekspresi dengan operasi aritmetika seperti (x, /, + , dan - ), misal kita
memiliki tabel penjualan_detail dengan data sebagai berikut:
+----+--------+-----------+------+--------------+--------+
| id | id_trx | id_barang | qty | harga_satuan | diskon |
+----+--------+-----------+------+--------------+--------+
| 1 | 1 | 9 | 1 | 46800 | 0 |
| 2 | 1 | 4 | 1 | 34800 | 0 |
| 3 | 1 | 6 | 2 | 33800 | 0.1 |
| 4 | 2 | 9 | 1 | 46800 | 0 |

Killer Trik Query MySQL 9


| 5 | 2 | 10 | 2 | 39800 | 0 |
+----+--------+-----------+------+--------------+--------+

Selanjutnya kita tampilkan data id_barang, qty, dan harga_satuan


selain itu kita tampilkan data sub_total dengan mengalikan nilai pada
kolom qty dengan harga_satuan. Query yang kita jalankan:

1. SELECT id, qty, harga_satuan, qty * harga_satuan AS


sub_total
2. FROM penjualan_detail

Hasil yang kita peroleh:


+----+------+--------------+-----------+
| id | qty | harga_satuan | sub_total |
+----+------+--------------+-----------+
| 1 | 1 | 46800 | 46800 |
| 2 | 1 | 34800 | 34800 |
| 3 | 2 | 33800 | 67600 |
| 4 | 1 | 46800 | 46800 |
| 5 | 2 | 39800 | 79600 |
+----+------+--------------+-----------+

Pada query diatas, pada klausa SELECT terdapat keyword AS.


Keyword ini digunakan untuk memberi nama alias pada
kolom, yang pada contoh diatas, nama kolom menjadi
sub_total. Jika tidak menggunakan kolom alias, maka nama
kolom adalah qty * harga_satuan

Kolom alias dapat ditulis dengan atau tanpa keyword AS, jika
tanpa keyword AS, pemberian nama kolom alias cukup diberi
jarak spasi, misal: qty * harga_satuan sub_total. Saya sendiri
lebih memilih menggunakan keyword AS karena mudah
diidentifikasi terutama pada query yang kompleks.

Selanjutnya kita tambahkan diskon untuk mendapatkan nilai


sub_total dengan formula qty x harga_satuan – diskon. Query yang
kita jalankan

10 BAB 1 Statemen SELECT


1. SELECT id, qty, harga_satuan
2. , qty * harga_satuan AS sub_total
3. , diskon
4. , qty * harga_satuan * diskon AS nilai_diskon
5. , qty * harga_satuan - ( qty * harga_satuan * diskon )
AS neto
6. FROM penjualan_detail

Hasil yang kita peroleh:


+----+------+--------------+-----------+--------+------------------+-------------------+
| id | qty | harga_satuan | sub_total | diskon | nilai_diskon | neto |
+----+------+--------------+-----------+--------+------------------+-------------------+
| 1 | 1 | 46800 | 46800 | 0 | 0 | 46800 |
| 2 | 1 | 34800 | 34800 | 0 | 0 | 34800 |
| 3 | 2 | 33800 | 67600 | 0.1 | 6760.00010073185 | 60839.99989926815 |
| 4 | 1 | 46800 | 46800 | 0 | 0 | 46800 |
| 5 | 2 | 39800 | 79600 | 0 | 0 | 79600 |
+----+------+--------------+-----------+--------+------------------+-------------------+

Pada contoh diatas, kolom nilai_diskon dan neto memiliki nilai


desimal (nilai di belakang koma) yang cukup panjang, untuk itu kita
perlu menyederhanakannya menggunakan fungsi ROUND, kita ubah
query nya menjadi:

1. SELECT id, qty, harga_satuan


2. , qty * harga_satuan AS sub_total
3. , diskon
4. , ROUND(qty * harga_satuan * diskon) AS nilai_diskon
5. , ROUND(qty * harga_satuan - ( qty * harga_satuan *
diskon )) AS neto
6. FROM penjualan_detail

Hasil yang kita peroleh:


+----+------+--------------+-----------+--------+--------------+-------+
| id | qty | harga_satuan | sub_total | diskon | nilai_diskon | neto |
+----+------+--------------+-----------+--------+--------------+-------+
| 1 | 1 | 46800 | 46800 | 0 | 0 | 46800 |
| 2 | 1 | 34800 | 34800 | 0 | 0 | 34800 |
| 3 | 2 | 33800 | 67600 | 0.1 | 6760 | 60840 |
| 4 | 1 | 46800 | 46800 | 0 | 0 | 46800 |
| 5 | 2 | 39800 | 79600 | 0 | 0 | 79600 |
+----+------+--------------+-----------+--------+--------------+-------+

Killer Trik Query MySQL 11


Ketika menggunakan operator aritmetika, jika diperlukan
tambahkan tanda kurung, terutama jika melibatkan operator
plus atau minus (+ -) dengan kali atau bagi (* /). Dengan tanda
kurung, ekspresi yang ada di dalam tanda kurung akan di
eksekusi terlebih dahulu.

1.3. Operator Perbandingan


Operator perbandingan digunakan untuk membandingkan dua nilai
atau ebih. Operator ini sering digunakan pada klausa WHERE.

Operator perbangingan yang sering digunakan adalah sama dengan


( = ) dan tidak sama dengan ( != ). Pada contoh sebelumnya kita telah
menggunakan operator ini untuk mengambil data mahasiswa
dengan jenis kelamin Laki Laki:

1. SELECT nama, jenis_kelamin


2. FROM mhs
3. WHERE jenis_kelamin = "L"

Selain sama dengan dan tidak sama dengan, kita juga dapat
menggunakan operator > (lebih besar), < (lebih kecil), >= (lebih besar
atau sama dengan), <= (lebih kecil atau sama dengan)

1.4. Nilai NULL


Dalam database, terdapat nilai dengan perlakuan khusus yaitu NULL,
nilai ini tidak sama dengan bilangan 0 maupun string kosong ''.

Dalam dunia database, NULL diartikan sebagai ketiadaan data / data


tidak terdefinisi, sehingga untuk mengujinya kita tidak dapat
menggunakan operator apapun seperti: nama_kolom = NULL

12 BAB 1 Statemen SELECT


Untuk menguji nilai NULL, kita hanya bisa menggunakan IS NULL dan
IS NOT NULL, misal kita memiliki data tabel mahasiswa sebagai
berikut:
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |
| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
| 008 | Haris | L | NULL |
+------+---------+---------------+------------+

Selanjutnya kita ambil data mahasiswa dengan kd_jurusan NULL

1. SELECT *
2. FROM mhs
3. WHERE kd_jurusan IS NULL

Hasil:
+------+-------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+-------+---------------+------------+
| 008 | Haris | L | NULL |
+------+-------+---------------+------------+

Sebaliknya, jika ingin mengambil data mahasiswa yang telah memilih


jurusan, yang artinya kolom kd_jurusan tidak bernilai NULL, maka
tinggal kita ubah klausa WHERE menjadi WHERE kd_jurusan IS NOT
NULL

Contoh lainnya ketika kita menggunakan keyword HAVING, misal


melanjutnya pembahasan pada klausa GROUP BY

1. SELECT kd_jurusan, COUNT(kd_jurusan) AS jml_mahasiswa


2. FROM mhs

Killer Trik Query MySQL 13


3. WHERE jenis_kelamin = "L"
4. GROUP BY kd_jurusan

Hasil:
+------------+---------------+
| kd_jurusan | jml_mahasiswa |
+------------+---------------+
| NULL | 0 |
| J001 | 2 |
| J002 | 1 |
+------------+---------------+

Selanjutnya kita ingin menampilkan kd_jurusan yang tidak memiliki


nilai NULL

1. SELECT kd_jurusan, COUNT(kd_jurusan) AS jml_mahasiswa


2. FROM mhs
3. WHERE jenis_kelamin = "L"
4. GROUP BY kd_jurusan
5. HAVING kd_jurusan IS NOT NULL

Hasil:
+------------+---------------+
| kd_jurusan | jml_mahasiswa |
+------------+---------------+
| J001 | 2 |
| J002 | 1 |
+------------+---------------+

Jika kita ingin menampilkan hanya kd_jurusan yang memiliki nilai


NULL, maka kita tinggal ubah klausa HAVING menjadi HAVING
kd_jurusan IS NULL

1.5. Operator OR dan AND


Ketika mengevaluasi nilai pada tabel menggunakan klausa WHERE atau
HAVING, maka kita dapat menggunakan operator logika (AND dan OR)

14 BAB 1 Statemen SELECT


Pada operator OR, kondisi bernilai benar (true) jika salah satu kondisi
bernilai true, misal:

1. SELECT kd_jurusan, nama, jenis_kelamin


2. FROM mhs
3. WHERE kd_jurusan = "J001" OR kd_jurusan IS NULL

Hasil:
+------------+---------+---------------+
| kd_jurusan | nama | jenis_kelamin |
+------------+---------+---------------+
| J001 | Charlie | P |
| J001 | Delta | L |
| J001 | Erdhi | L |
| NULL | Haris | L |
+------------+---------+---------------+

Killer Trik Query MySQL 15


Pada contoh diatas, mahasiswa yang memiliki kd_jurusan 001 atau
kd_jurusan bernilai NULL akan ditampilkan (salah satu bernilai true
baik kd_jurusan bernilai 001 atau kd_jurusan bernilai NULL). Agar
lebih jelas, perhatikan ilustrasi berikut:

Gambar 1.1 Ilustrasi Operator Logika OR

Ingat kembali bahwa ketika menjalankan klausa WHERE, maka baris


pada tabel akan di evaluasi satu per satu, seperti pada gambar
diatas.

Pada operator AND, kondisi bernilai true, jika keduanya bernilai true,
contoh:

16 BAB 1 Statemen SELECT


1. SELECT kd_jurusan, nama, jenis_kelamin
2. FROM mhs
3. WHERE kd_jurusan = "J001" AND jenis_kelamin = "L"

Hasil:
+------------+-------+---------------+
| kd_jurusan | nama | jenis_kelamin |
+------------+-------+---------------+
| J001 | Delta | L |
| J001 | Erdhi | L |
+------------+-------+---------------+

Pada contoh diatas, hanya mahasiswa yang memiliki kd_jurusan J001


dan jenis_kelamin L yang akan ditampilkan, (keduanya bernilai true)
untuk alur logikanya mirip dengan ilustrasi gambar I.1

Prioritas
Ketika orator OR dan AND digunakan secara bersama sama, maka
operator AND memiliki prioritas lebih tinggi sehingga akan dievaluasi
terlebih dahulu, sehingga jika tidak hati hati, bisa jadi hasil yang kita
peroleh tidak sesuai yang kita harapkan.

Misal kita akan menampilkan semua data siswa wanita dengan


kd_jurusan NULL atau kd_jurusan J001

Bagaimana klausa WHERE nya?

Umumnya kita akan menulis begini:

1. SELECT kd_jurusan, nama, jenis_kelamin


2. FROM mhs
3. WHERE kd_jurusan IS NULL OR kd_jurusan = "J001" AND
jenis_kelamin = "P"

Hasil yang kita peroleh:

Killer Trik Query MySQL 17


+------------+---------+---------------+
| kd_jurusan | nama | jenis_kelamin |
+------------+---------+---------------+
| J001 | Charlie | P |
| NULL | Haris | L |
+------------+---------+---------------+

Apakah sudah benar?

Yup, ternyata mahasiswa dengan jenis kelamin L tetap ditampilkan,


padahal kita ingin menampilkan hanya mahasiswa perempuan (P),
kenapa bisa seperti itu?

Hal ini karena operator AND akan dievaluasi terlebih dahulu,


sehingga pertama tama MySQL akan mencari data dengan
kd_jurusan 001 dan jenis_kelamin P ( kd_jurusan = "J001" AND
jenis_kelamin = "P" ) yang menghasilkan baris pertama, kemudian
mencari semua mahasiswa yang memiliki kd_jurusan NULL
(kd_jurusan IS NULL) yang menghasilkan baris ke dua.

Bagaimana mengatasinya?

Untuk mengatasinya, gunakan tanda kurung pada operator yang


ingin didahulukan, yang dalam hal ini adalah operator OR, ingat
prinsip bahwa semua yang ada didalam tanda kurung akan di
evaluasi terlebih dahulu.

Kita ubah querynya menjadi:

1. SELECT kd_jurusan, nama, jenis_kelamin


2. FROM mhs
3. WHERE (kd_jurusan IS NULL OR kd_jurusan = "J001") AND
jenis_kelamin = "P"

Hasil:

18 BAB 1 Statemen SELECT


+------------+---------+---------------+
| kd_jurusan | nama | jenis_kelamin |
+------------+---------+---------------+
| J001 | Charlie | P |
+------------+---------+---------------+

Semua yang ada di dalam tanda kurung akan di eksekusi


terlebih dahulu, baik operator, fungsi, ekspresi dll.

Selalu gunakan tanda kurung ketika menggunakan operator


OR dan AND secara bersama sama.

Killer Trik Query MySQL 19


Halaman ini sengaja dikosongkan
Jagowebdev.com

20 BAB 1 Statemen SELECT


BAB 2 Kunci Menguasai SELECT
Seperti telah disampaikan pada awal bab ini, statemen SELECT
merupakan statemen yang paling rumit dan kompleks karena
variasinya sangat banyak sekali.

Namun demikian, seberapapun rumitnya kasus yang Anda hadapi,


Anda akan mampu memecahkannya jika Anda menguasai konsep
bagaimana statemen SELECT dieksekusi.

Dalam internal database, eksekusi statemen SELECT membutuhkan


proses yang rumit, banyak yang akan dipertimbangkan, terutama jika
query yang dieksekusi bentuknya kompleks

Pada BAB ini akan kita membahas konsep eksekusi ini. Konsep ini
tidak mencerminkan 100% cara MySQL menangani statemen
SELECT namun akan dapat memudahkan Anda memecahkan
berbagai masalah pengambilan data.

N.B. konsep yang disampaikan disini merupakan hasil penelitian


saya pribadi sehingga mungkin berbeda dengan yang ada di
referensi lain.

2.1. Memahami Urutan Eksekusi


Hal terpenting yang pertama harus dipahami adalah memahami
urutan eksekusi statemen SELECT.

Hal ini sangat penting karena akan memudahkan kita untuk


mendapatkan gambaran umum urutan / langkah yang akan kita
ambil dalam menyusun query.

So, bagaimana urutannya?

Killer Trik Query MySQL 21


Pertama…
Ketika MySQL mengeksekusi statemen SELECT, maka pertama tama
MySQL akan mengeksekusi klausa FROM dan WHERE (jika ada)
sehingga menghasilkan intermediate table atau tabel antara.

Tabel ini bisa berupa tabel riil (Jika klausa FROM menunjuk tabel
pada database) atau tabel sementara (temporary table) – jika tabel
hasil klausa FROM tidak ada pada database, seperti penggunaan
JOIN atau subquery.

Klausa WHERE (jika ada) akan dieksekusi setelah tabel hasil klausa
FROM terbentuk, gunanya untuk membatasi jumlah data yang
diambil.

Kenapa klausa FROM dulu?

Logikanya begini…

Jika klausa SELECT yang dijalankan terlebih dahulu, maka MySQL


akan mencari kolom yang pada klausa SELECT ke semua tabel yang
ada pada database.

Hal ini tentu saja sangat tidak efisien. Dengan memilih atau
membentuk tabel terlebih dahulu maka akan jauh lebih efektif ketika
memilih kolom mana yang akan diambil.

Bagian pertama ini sangat vital yang akan menentukan


langkah selanjutnya, so… pahami dengan baik

Tabel hasil klausa FROM ini harus berupa satu tabel


bagaimanapun kompleksnya data yang digunakan, sehingga
jika sumber data terdiri dari beberapa tabel maka tabel
tersebut harus kita gabungkan terlebih dahulu menjadi satu
menggunakan JOIN atau UNION

22 BAB 2 Kunci Menguasai SELECT


Kedua…
Selanjutnya MySQL mengeksekusi klausa SELECT, eksekusi ini
bertujuan:

1. Untuk membuat kolom baru jika kolom tersebut belum ada pada
tabel hasil klausa FROM, misal kolom hasil perkalian atau kolom
hasil fungsi agregasi.

2. Untuk mengambil kolom yang akan ditampilkan pada tabel


output. Penting diperhatikan bahwa pengambilan kolom ini
dilakukan terakhir setelah semua klausa selesai dieksekusi.

Ketiga…
Setelah selesai pada klausa SELECT, MySQL akan mengeksekusi
klausa opsional lain urut mulai dari GROUP BY, dst.. hingga LIMIT

Terakhir…
Setelah semua dieksekusi, MySQL akan menghasilkan tabel output
dengan kolom sesuai yang didefinisikan pada klausa SELECT

Killer Trik Query MySQL 23


Ilustrasinya adalah sebagai berikut:

Gambar 2.1 Ilustrasi eksekusi statemen SELECT

Gunakan konsep alur diatas untuk menyelesaikan setiap


kasus query yang kita hadapi, dijamin akan jauh lebih cepat
dan mudah.

Untuk lebih memahami alur diatas, mari kita langsung praktek. Misal
kita memiiki tabel penjualan sebagai berikut:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

24 BAB 2 Kunci Menguasai SELECT


Selanjutnya kita tampilkan data 3 penjualan terbesar di tahun 2017,
dengan menampilkan kolom id_pelanggan, tgl_trx, dan total_trx
sehingga terbentuk tabel output sebagai berikut:
+--------------+------------+-----------+
| id_pelanggan | tgl_trx | total_trx |
+--------------+------------+-----------+
| 0 | 2017-04-10 | 259000 |
| 1 | 2017-03-02 | 192000 |
| 1 | 2017-03-10 | 186000 |
+--------------+------------+-----------+

Bagaimana querynya?

Mari kita bahas…

Pertama…
Ingat alur query, pertama identifikasi tabel pada klausa FROM, jika
perlu batasi data dengan klausa WHERE.

Pada kasus diatas kita akan menampilkan data penjualan tahun


2017, maka kita ambil tabel penjualan dan dengan klausa WHERE
kita batasi hanya data tahun 2017 yang diambil, mari kita tes dengan
menggunakan klausa SELECT *

1. SELECT *
2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017;

Hasilnya:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
+--------+--------------+------------+-----------+

Killer Trik Query MySQL 25


Pada query diatas, fungsi YEAR digunakan untuk mengambil data
tahun, kita akan membahas lebih jauh tentang fungsi ini pada BAB
VI Menguasai Fungsi Scalar.

Hasil diatas sudah sesuai dengan yang kita harapkan, mari kita
lanjutkan…

Kedua…
Selanjutnya kita menuju ke klausa SELECT, kita pilih kolom yang ingin
kita tampilkan, yaitu kolom id_pelanggan, tgl_trx, dan total_trx,
sehingga klausa SELECT nya menjadi:

1. SELECT id_pelanggan, tgl_trx, total_trx


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017

Ketiga…
Terakhir, karena kita akan mengurutkan data berdasarkan penjualan
terbesar, maka kita gunakan klausa ORDER BY yang kita terapkan
pada pada kolom total_trx sebagai berikut:

1. SELECT id_pelanggan, tgl_trx, total_trx


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. ORDER BY total_trx DESC

Query diatas akan menghasilkan tabel dengan data yang diurutkan


berdasarkan jumlah pembayaran (kolom total_byr) mulai dari yang
terbesar

Selanjutnya kita hanya akan mengambil tiga terbesar, sehingga kita


tambahkan klausa LIMIT sebagai berikut:

26 BAB 2 Kunci Menguasai SELECT


1. SELECT id_pelanggan, tgl_trx, total_trx
2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. ORDER BY total_trx DESC
5. LIMIT 3

Hasil yang kita peroleh:


+--------------+------------+-----------+
| id_pelanggan | tgl_trx | total_trx |
+--------------+------------+-----------+
| 0 | 2017-04-10 | 259000 |
| 1 | 2017-03-02 | 192000 |
| 1 | 2017-03-10 | 186000 |
+--------------+------------+-----------+

Nah, hasilnya sudah pas dengan yang kita inginkan.

Untuk lebih jelasnya, perhatikan ilustrasi berikut:

Killer Trik Query MySQL 27


Gambar 2.2 Ilustrasi Proses Pengambilan Data

28 BAB 2 Kunci Menguasai SELECT


Exception 1: Fungsi Agregasi
Pada eksekusi statemen SELECT, terdapat beberapa hal atau
pengecualian (exception) yang menyebabkan urutan proses
eksekusi berubah.

Apa penyebabnya? penyebabnya adalah adanya fungsi agregasi


pada klausa SELECT.

Note: Kita akan membahas lebih jauh mengenai fungsi agregasi ini
pada bab V. Menguasai Fungsi Agregasi

Jika pada klausa SELECT terdapat fungsi agregasi –-dan pastinya kita
akan sering menggunakan fungsi ini-- maka fungsi ini akan dijalankan
belakangan bersama-sama dengan klausa GROUP BY

Sebagai contoh: pada query sebelumnya kita tambahkan fungsi SUM


pada klausa SELECT untuk menjumlahkan nilai total transaksi, selain
itu juga kita batasi jumlah baris yang ditampilkan sebanyak 2 baris,
query nya adalah sebagai berikut:

1. SELECT id_pelanggan, tgl_trx, SUM(total_trx)


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. GROUP BY id_pelanggan
5. ORDER BY SUM(total_trx) DESC
6. LIMIT 2

Hasil yang kita peroleh:


+--------------+------------+----------------+
| id_pelanggan | tgl_trx | SUM(total_trx) |
+--------------+------------+----------------+
| 1 | 2017-03-02 | 378000 |
| 0 | 2017-04-10 | 259000 |
+--------------+------------+----------------+

Killer Trik Query MySQL 29


Urutan eksekusi query diatas sama seperti yang telah kita pelajari,
namun ada sedikit perubahan, urutannya adalah sebagai berikut:

1. Klausa FROM dan WHERE

2. Klausa SELECT (untuk membuat kolom baru – jika ada). Fungsi


SUM tidak dieksekusi.

3. Klausa GROUP BY bersama dengan fungsi SUM

4. Klausa ORDER BY, dan

5. Klausa LIMIT.

Pada urutan diatas, terlihat bahwa fungsi agregasi SUM di jalankan


bersama dengan klausa GROUP BY lebih tepatnya setelah data
diurutkan.

Kenapa demikian?

Hal ini untuk memudahkan pengelompokan data (ketika


menjalankan fungsi agregasi - SUM), karena data sudah diurutkan
secara ascending (ingat kembali bahwa ketika menjalankan klausa
GROUP BY, data akan diurutkan terlebih dahulu)

Untuk lebih jelasnya, perhatikan ilustrasi berikut:

30 BAB 2 Kunci Menguasai SELECT


Gambar 2.3 Ilustrasi Eksekusi Statemen SELECT Yang Mengandung Fungsi
Agregasi SUM

Pada ilustrasi diatas terlihat bahwa pada bagian GROUP BY


id_pelanggan + SUM(total_trx) data diurutkan berdasarkan kolom

Killer Trik Query MySQL 31


id_pelanggan baru kemudian di kelompokkan (GROUP BY) dan di
jumlahkan (SUM), sehingga data menjadi urut berdasarkan
id_pelanggan, betul kan? Baru kemudian diurutkan berdasarkan
kolom SUM(total_trx)

Nah setelah Anda memahami contoh diatas, mari kita sedikit


kembangkan query nya, mari kita tambahkan fungsi COUNT pada
klausa SELECT, sehingga querynya menjadi:

1. SELECT id_pelanggan, COUNT(id_trx), SUM(total_trx)


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. GROUP BY id_pelanggan
5. ORDER BY total_trx DESC
6. LIMIT 2

Jika kita jalankan query diatas akan menghasilkan output:


+--------------+---------------+----------------+
| id_pelanggan | COUNT(id_trx) | SUM(total_trx) |
+--------------+---------------+----------------+
| 0 | 1 | 259000 |
| 1 | 2 | 378000 |
+--------------+---------------+----------------+

Fungsi COUNT diatas digunakan untuk menghitung banyaknya baris


pada kolom id_trx yang tidak mengandung nilai NULL.

Kita hitung banyaknya baris untuk menghitung banyaknya transaksi


karena banyaknya transaksi tercermin dari banyaknya baris yang
ada.

Lebih jauh tentang fungsi agregasi kita pelajari pada BAB V


Menguasai Fungsi Agregasi

Selanjutnya, bagaimana alur querynya?

32 BAB 2 Kunci Menguasai SELECT


Alurnya sama persis dengan gambar II. 3 Bedanya ada tambahan
kolom COUNT(id_trx)

2.2. Konsekuensi Urutan Eksekusi


Setelah memahami urutan eksekusi statmen SELECT, maka kita
perlu memahami konsekuensi konsekuensi yang terjadi ketika
masing-masing klausa dieksekusi.

Apa saja konsekuensinya?

Dari masing masing tahapan eksekusi, jika diperlukan, MySQL akan


membuat temporary tabel. Ketika temporary tabel ini sudah
terbentuk, konsekusensinya kita dapat menggunakan nama kolom
dari tabel tersebut.

Bingung?

Baiklah, ambil contoh seperti ini, kita memiliki tabel penjualan_detail


sebagai berikut:
+----+--------+-----------+------+--------------+--------+
| id | id_trx | kd_barang | qty | harga_satuan | diskon |
+----+--------+-----------+------+--------------+--------+
| 1 | 1 | 1 | 1 | 76000 | 0 |
| 2 | 1 | 3 | 1 | 35000 | 0 |
| 3 | 1 | 5 | 2 | 45000 | 0.1 |
| 4 | 2 | 1 | 1 | 70000 | 0 |
| 5 | 2 | 2 | 2 | 55000 | 0 |
+----+--------+-----------+------+--------------+--------+

Selanjutnya kita akan urutkan data berdasarkan total harga (qty x


harga_satuan) mulai dari yang tersbesar. Query yang kita jalankan
adalah sebagai berikut:

1. SELECT id, qty, harga_satuan, qty * harga_satuan AS total


2. FROM penjualan_detail
3. ORDER BY total DESC

Killer Trik Query MySQL 33


Hasil yang kita peroleh:
+----+------+--------------+--------+
| id | qty | harga_satuan | total |
+----+------+--------------+--------+
| 5 | 2 | 55000 | 110000 |
| 3 | 2 | 45000 | 90000 |
| 1 | 1 | 76000 | 76000 |
| 4 | 1 | 70000 | 70000 |
| 2 | 1 | 35000 | 35000 |
+----+------+--------------+--------+

Pada query diatas, kita menggunakan keyword AS. Seperti


yang telah kita pelajari, keyword ini digunakan untuk
memberi nama alias pada kolom.

Seperti yang kita pelajari, maka urutan query diatas adalah:

Pertama…
MySQL akan mengeksekusi klausa FROM, pada kasus ini, MySQL
akan menggunakan tabel riil karena tabel penjualan_detail sudah
ada di database

Kedua…
Selanjutnya MySQL akan mengeksekusi klausa SELECT, untuk
membuat kolom baru (jika ada). Pada contoh diatas terdapat kolom
baru yaitu kolom total.

Pada tahap ini, MySQL akan membat temporary tabel yang isinya
kolom pada tabel penjualan_detai dan kolom total sebagai berikut:
+----+--------+-----------+------+--------------+--------+--------+
| id | id_trx | kd_barang | qty | harga_satuan | diskon | total |
+----+--------+-----------+------+--------------+--------+--------+
| 1 | 1 | 1 | 1 | 76000 | 0 | 76000 |
| 2 | 1 | 3 | 1 | 35000 | 0 | 35000 |
| 3 | 1 | 5 | 2 | 45000 | 0.1 | 90000 |
| 4 | 2 | 1 | 1 | 70000 | 0 | 70000 |
| 5 | 2 | 2 | 2 | 55000 | 0 | 110000 |
+----+--------+-----------+------+--------------+--------+--------+

34 BAB 2 Kunci Menguasai SELECT


Selanjutnya temporary tabel ini akan digunakan untuk menjalankan
proses pada tahap berikutnya.

Ketiga…
Selanjutnya, MySQL akan mengeksekusi klausa opsional yang ada,
yang yang dalam contoh kali ini klausa ORDER BY.

Klausa tersebut menggunakan kolom total untuk mengurutkan data


secara descending (ORDER BY total DESC), dan karena kolom total ini
sudah terbentuk pada proses sebelumnya maka kita dapat
menggunakannya.

Catatan 1…
Kolom alias baru tersedia mulai klausa GROUP BY dst… tidak bisa
digunakan pada proses (tahapan) sebelumnya yaitu SELECT

Kenapa? Karena kolom tersebut baru terbentuk setelah klausa


SELECT selesai dieksekusi

Sebagai contoh, jalankan query berikut:

1. SELECT id, qty, harga_satuan, qty * harga_satuan AS total


2. , total * 0.1 AS diskon
3. FROM penjualan_detail_copy
4. ORDER BY total DESC

Maka kita akan mendapati error:

ERROR 1054 (42S22): Unknown column 'total' in 'field list'

Error tersebut terjadi karena kolom total belum terbentuk.

Contoh lain: penggunaan kolom alias

Killer Trik Query MySQL 35


1. SELECT id, qty, harga_satuan, qty * harga_satuan AS total
2. FROM penjualan_detail_copy
3. GROUP BY total DESC

Hasil:
+----+------+--------------+--------+
| id | qty | harga_satuan | total |
+----+------+--------------+--------+
| 5 | 2 | 55000 | 110000 |
| 3 | 2 | 45000 | 90000 |
| 1 | 1 | 76000 | 76000 |
| 4 | 1 | 70000 | 70000 |
| 2 | 1 | 35000 | 35000 |
+----+------+--------------+--------+

Pada contoh diatas, karena klausa GROUP BY dieksekusi setelah


klausa SELECT, maka kita dapat menggunakan kolom alias

Pendalaman materi…
Untuk lebih memahami proses ini, misal dari contoh diatas, kita akan
mengambil data dengan total harga lebih dari 50.000

Mari kita jalankan query berikut:

1. SELECT id, qty, harga_satuan, qty * harga_satuan AS total


2. FROM penjualan_detail
3. WHERE total > 50000
4. ORDER BY total DESC

Maka pasti kita akan mendapati error:


ERROR 1054 (42S22): Unknown column 'total' in 'where clause'

Sudah tahu kan kenapa muncul error seperti itu?

Yup. Karena klausa FROM dan WHERE akan di eksekusi pertama kali
dan pada tahap itu, hanya tersedia kolom tabel penjualan_detail dan
tidak ada kolom total

36 BAB 2 Kunci Menguasai SELECT


Solusinya bagaimana?

Mau tidak mau kita harus menggunakan kolom yang ada yaitu kolom
qty dan harga_satuan

Jalankan query berikut:

1. SELECT id, qty, harga_satuan, qty * harga_satuan AS total


2. FROM penjualan_detail
3. WHERE qty * harga_satuan > 50000
4. ORDER BY total DESC

Hasilnya adalah sebagai berikut:


+----+------+--------------+--------+
| id | qty | harga_satuan | total |
+----+------+--------------+--------+
| 5 | 2 | 55000 | 110000 |
| 3 | 2 | 45000 | 90000 |
| 1 | 1 | 76000 | 76000 |
| 4 | 1 | 70000 | 70000 |
+----+------+--------------+--------+

Mungkin anda bertanya…., bagaimana data bisa diambil hanya yang


total nya diatas 50.000?

Lihat kembali BAB I, ketika MySQL mengeksekusi klausa WHERE,


maka setiap baris pada tabel akan dievaluasi satu per satu dan
hanya diambil baris yang memenuhi kriteria pada klausa WHERE

Pada query diatas, MySQL akan mengalikan qty dengan


harga_satuan di setiap baris pada tabel penjualan_detail dan hanya
diambil baris yang hasil perkalian nya diatas 50.000

Catatan 2: Fungsi Agregasi


Lagi lagi terdapat hal di luar “pakem” dan lagi lagi terkait fungsi
agregasi, yaitu jika kolom alias tersebut terbentuk dari fungsi

Killer Trik Query MySQL 37


agregasi, maka kolom tersebut baru terbentuk setelah klausa
GROUP BY selesai dieksekusi.

Kenapa?

Karena fungsi agregasi dieksekusi bersama dengan klausa GROUP


BY (seperti yang telah kita bahas pada bagian sebelumnya) sehingga
kolom tersebut baru terbentuk setelah klausa GROUP BY selesai
dieksekusi.

Dengan demikian, kita tidak dapat menggunakannya pada klausa


GROUP BY, melainkan mulai klausa ORDER BY dan seterusnya,
contoh:

1. SELECT id_trx, id_pelanggan, SUM(total_trx) AS total


2. FROM penjualan
3. GROUP BY total

Jika query tersebut dijalankan maka kita akan mendapatki pesan


error:

SQL Error (1056): Can't group on 'total'

Pada contoh diatas, karena kolom total merupakan kolom alias dan
baru terbentuk setelah klausa GROUP BY selesai dieksekusi, maka
tidak dapat digunakan pada klausa GROUP BY

Kasus seperti ini jarang sekali terjadi, sehingga tidak perlu


difikirkan terlalu menadalam cukup dijadikan pengetahuan
saja.

38 BAB 2 Kunci Menguasai SELECT


2.3. Identifikasikan Bentuk Tabel
Pada bagian ini kita akan membahas lebih dalam bagaimana
mengidentifikasi bentuk tabel hasil eksekusi klausa FROM

Bentuk tabel ini sangat penting karena akan menentukan tabel awal
yang akan digunakan pada tahap berikutnya, untuk itu kita perlu
meng identifikasikan bentuk tabel ini dengan benar

Pada bagian sebelumnya telah sedikit dibahas bagaimana bentuk


tabel hasil klausa FROM ini, pada pembahasan tersebut bentuk tabel
berupa tabel riil yaitu tabel penjualan_detail (FROM penjualan_detail)

Pada banyak kasus, kita perlu menggunakan dua atau lebih tabel,
dan karena hasil tabel pada klausa FROM ini harus berupa satu tabel,
maka kita harus menggabungkan tabel tabel tersebut menjadi satu.

Terkait penggabungan ini, terdapat dua bentuk yang dapat


digunakan yaitu menggunakan JOIN dan UNION, keduanya akan kita
bahas pada BAB tersendiri.

Pertanyaan selanjutnya, kapan menggunakan JOIN dan kapan


menggunakan UNION?

Untuk memutuskan apakah menggunakan JOIN atau UNION,


perhatikan kondisi berikut:

1. JOIN. Gunakan JOIN jika kolom pada tabel hasil klausa FROM
terdapat kolom dari tabel yang digabungkan, atau bisa juga jika
kedua tabel saling berhubungan (ada key antar tabel - foreign
key)

2. UNION. Gunakan UNION jika tabel hasil klausa FROM memiliki


kolom yang sama dengan tabel yang digabungkan atau jika kita

Killer Trik Query MySQL 39


perlu data pada kedua tabel namun keduanya tidak saling
berhubungan (tidak ada key antar tabel).

Praktik di lapangan kita akan sering menggunakan JOIN dan jarang


(hampir tidak pernah) menggunakan UNION.

Selanjutnya mari kita belajar dari contoh…

Kali ini kita akan menggunakan JOIN

Note: Jika Anda belum memahami join, tidak masalah, cukup ikuti
saja dulu pembahasannya.

Misal kita memiliki dua buah tabel, yaitu tabel penjualan dan
pelanggan sebagai berikut:
penjualan
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+
pelanggan
+--------------+---------+-----------+---------+
| id_pelanggan | nama | alamat | id_staf |
+--------------+---------+-----------+---------+
| 1 | Alfa | Jakarta | 1 |
| 2 | Beta | Semarang | 1 |
| 3 | Charlie | Surabaya | 2 |
| 4 | Delta | Surakarta | 3 |
+--------------+---------+-----------+---------+

Selanjutnya kita ingin menampilkan data penjualan beserta nama


pelanggannya dengan output sebagai berikut:
+------+------------+-----------+
| nama | tgl_trx | total_trx |
+------+------------+-----------+
| Alfa | 2017-03-02 | 192000 |

40 BAB 2 Kunci Menguasai SELECT


| Alfa | 2017-03-10 | 186000 |
| Beta | 2017-04-05 | 110000 |
| Beta | 2016-11-10 | 256000 |
| NULL | 2017-04-10 | 259000 |
+------+------------+-----------+

Bagaimana querynya?

Sesuai dengan prinsip yang telah kita bahas, maka pertama kita buat
gabungan tabel melalui klausa FROM, selanjutnya kita ambil kolom
melalui klausa SELECT.

Pertama…
Karena kita menggunakan lebih dari satu tabel, maka kita perlu
identifikase tabel output…

Pada tabel output terdapat kolom nama yang berasal dari tabel
pelanggan, dan kolom tgl_trx dan total_trx yang berasal dari tabel
penjualan.

Nah karena kolom pada tabel output ada pada kedua tabel, maka
kita harus menggabungkan kedua tabel tersebut.

Selanjutnya menggunakan JOIN atau UNION?

Jika kita perhatikan, kolom yang akan kita tampilkan adalah kolom
nama, tgl_trx, dan total_trx ketiga kolom tersebut ada pada
gabungan kedua tabel, maka tabel hasil klausa FROM harus memuat
ketiga kolom tersebut, selain itu, kedua tabel juga saling
berhubungan pada kolom id_pelanggan, oleh karena itu kita
gunakan JOIN

Mari kita gabungkan kedua tabel menggunakan JOIN, jalankan query


berikut:

Killer Trik Query MySQL 41


1. SELECT *
2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan);

Hasilnya adalah:
+--------------+--------+------------+-----------+------+----------+---------+
| id_pelanggan | id_trx | tgl_trx | total_trx | nama | alamat | id_staf |
+--------------+--------+------------+-----------+------+----------+---------+
| 1 | 1 | 2017-03-02 | 192000 | Alfa | Jakarta | 1 |
| 1 | 2 | 2017-03-10 | 186000 | Alfa | Jakarta | 1 |
| 2 | 4 | 2017-04-05 | 110000 | Beta | Semarang | 1 |
| 2 | 5 | 2016-11-10 | 256000 | Beta | Semarang | 1 |
| 0 | 3 | 2017-04-10 | 259000 | NULL | NULL | NULL |
+--------------+--------+------------+-----------+------+----------+---------+

Kedua…
Selanjutnya kita definisikan kolom pada klausa SELECT, sehingga
querynya menjadi:

1. SELECT nama, tgl_trx, total_trx


2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan);

Hasil:
+------+------------+-----------+
| nama | tgl_trx | total_trx |
+------+------------+-----------+
| Alfa | 2017-03-02 | 192000 |
| Alfa | 2017-03-10 | 186000 |
| Beta | 2017-04-05 | 110000 |
| Beta | 2016-11-10 | 256000 |
| NULL | 2017-04-10 | 259000 |
+------+------------+-----------+

Bagaimana alur eksekusi querynya?

Alurnya Sama persis dengan yang telah kita bahas bada bagian
sebelumnya. Langsung saja, perhatikan ilustrasi berikut:

42 BAB 2 Kunci Menguasai SELECT


Gambar 2. 4 Ilustrasi alur eksekusi statemen SELECT yang terdapat klausa
JOIN

Pada ilustrasi diatas, terlihat bahwa MySQL membuat temporary


tabel yang berisi gabungan tabel penjualan dan pelanggan,
temporary tabel ini perlu dibuat karena tabel hasil klausa FROM
merupakan hasil dari penggabungan tabel, sehingga tidak mungkin
ada di database.

2.4. Pendefinisian Kolom Baru


Setelah kita memahami bagaimana statemen SELECT dieksekusi dan
bagaimana mendefinisikan tabel pada klausa FROM, selanjutnya kita

Killer Trik Query MySQL 43


harus dapat mengidentifikasi kolom pada tabel output yang akan
kita tampilkan.

Pendefinisian kolom ini sangat bervariasi tergantung kondisi


dilapangan, kolom bisa berupa existing column (kolom yang sudah
ada - kolom yang berasal dari tabel hasil klausa FROM), maupun
kolom baru yang berasal dari nilai skalar, string, fungsi, ekspresi, dll.

Yang terpenting adalah bagaimanapun rumitnya penghitungan yang


dilaukan, semua kolom pada tabel output harus ada pada klausa
SELECT, perhatikan ilustrasi berikut:

Gambar 2.5. Ilustrasi Jenis Kolom Pada Klausa SELECT

Pada ilustrasi diatas terlihat bahwa setiap yang kita tulis pada
statemen select (dengan pemisah tanda koma) akan selalu
menghasilkan kolom, baik nilai skalar (nilai 1) ekspresi berupa
operasi aritmatika, maupun nama kolom tabel hasil klausa FROM.

Berdasarkan hal tersebut diatas, dapat disimpulkan bahwa


jika kita ingin menambahkan kolom yang tidak ada pada

44 BAB 2 Kunci Menguasai SELECT


tabel hasil klausa FROM, maka kita harus mendefinisikannya
pada klausa SELECT

2.5. Eksekusi Baris


Selanjutnya, perlu dipahami juga bahwa setiap baris pada tabel yang
terbentuk oleh kalusa FROM akan dieksekusi sesuai dengan apa
yang di definisikan pada klausa SELECT.

Hal ini sesuai dengan prinsip yang telah kita pelajari bahwa ketika
mengeksekusi klausa SELECT, maka jika diperlukan, MySQL akan
membuat kolom baru, nah isi dari kolom baru ini merupakan hasil
eksekusi dari setiap baris yang ada pada tabel hasil klausa FROM

Perhatikan ilustrasi berikut:

Gambar 2.6. Ilustrasi Eksekusi Klausa SELECT

Pada gambar diatas terlihat bahwa untuk menghasilkan nilai pada


kolom output (kolom baru), setiap baris yang ada pada tabel
penjualan_detail akan diseksekusi sesuai dengan yang ada pada
klausa SELECT.

Killer Trik Query MySQL 45


2.6. Pembatas Tampilan Data
Konsep terakhir adalah mengetahui hal-hal yang menyebabkan data
tidak ditampilkan semua.

Maksudnya apa?

Secara default, ketika mengeksekusi statemen SELECT, maka


semua baris pada tabel hasil akan ditampilkan, kecuali jika terdapat
klausa:

1. WHERE;

2. GROUP BY;

3. LIMIT; dan

4. Fungsi agregasi pada klausa SELECT.

Sebenarnya tidak ada konsep baru disini, karena dengan


mengetahui arti dari klausa diatas (telah dibahas pada BAB I)
otomatis akan tahu bahwa ada pembatasan data yang ditampikan.

Namun demikian, hal ini perlu ditekankan lagi, karena pada kasus
yang rumit, kita bingung dan ragu apa yang harus dilakukan.

2.7. Poin Penting


Dari berbagai pembahasan diatas, dapat disimpulkan bahwa
terdapat beberapa hal penting yang perlu dipahami yaitu:

1. Memahami urutan eksekusi query. Urutannya adalah: FROM,


WHERE, SELECT, GROUP BY, ORDER BY, HAVING, dan LIMIT

2. Ketika menyusun query, pertama tama kita perlu


mendefinisikan tabel yang akan terbentuk pada eksekusi klausa

46 BAB 2 Kunci Menguasai SELECT


FROM, jika data melibatkan lebih dari satu tabel, gabungkan
tabel tersebut menjadi satu menggunakan JOIN atau UNION.

3. Jika ada kolom baru yang tidak ada pada tabel hasil eksekusi
klausa FROM, definisikan kolom tersebut pada klausa SELECT.

4. Kolom baru tersebut terbentuk setelah klausa SELECT selesai


dieksekusi, sehingga baru dapat digunakan pada klausa
GROUP BY, dst… Khusus untuk kolom hasil fungsi Agregasi,
seperti SUM(), dan COUNT(), maka kolom ini baru terbentuk
setelah klausa GROUP BY selesai dieksekusi, sehingga baru
dapat digunakan mulai klausa ORDER BY, dst…

5. Ketika mengeksekusi klausa SELECT dan WHERE maka setiap


baris pada tabel hasil eksekusi klausa FROM akan dieksekusi.

Killer Trik Query MySQL 47


Halaman ini sengaja dikosongkan
Jagowebdev.com

48 BAB 2 Kunci Menguasai SELECT


BAB 3 Ekspresi Logika
MySQL menyediakan dua bentuk ekspresi logika, yaitu ekspresi IF
dan CASE. Kedua ekspresi ini memiliki fungsi yang sama yaitu
digunakan untukk menguji suatu ekspresi, bedanya IF hanya bisa
digunakan unntuk menguji satu ekspresi, sedangkan CASE dapat
menguji lebih dari satu ekspresi

3.1. Ekspresi IF
Ekspresi logika IF berbentuk fungsi (menggunakan tanda kurung)
dan hanya dapat digunakan untuk menguji satu ekspresi.

Satu ekspresi artinya jika ekspresi benar maka jalankan statemen


tertentu jika salah jalankan statemen lain, dalam bahasa
pemorograman umum tampak seperti ini:

1. if (ekspresi) {
2. ...
3. } else {
4. ...
5. }

Contoh statemen dengan lebih dari satu ekspresi (tidak bisa


menggunakan fungsi IF):

1. if (ekspresi) {
2. ...
3. } elseif (ekspresi_lain) {
4. ...
5. }

Pada MySQL, format penulisan fungsi IF adalah sebagai berikut:

Killer Trik Query MySQL 49


IF(ekspresi, true, false)

Bagian true akan dieksekusi jika ekspresi bernilai benar dan bagian
false dieksekusi jika ekspresi bernilai salah.

Fungsi ini sangat powerfull dan sebagian besar query yang


mengandung logika dapat diselesaikan menggunakan fungsi ini,
untuk itu kuasai fungsi ini dengan baik.

Fungsi ini dapat berbentuk nested IF, yaing artinya IF didalam IF,
bentuknya seperti ini:

IF(ekspresi, true, IF(ekspresi_false, true, false))

Kita tidak membahas lebih jauh mengenai bentuk ini karena akan
membingungkan, selain itu, lebih mudah menggunkaan ekspresi
CASE dari pada nested IF.

Memahami eksekusi fungsi IF


Seperti yang telah kita bahas pada bab Kunci Menguasai SELECT,
ketika mengeksekusi klausa SELECT, MySQL akan memeriksa satu
per satu baris pada tabel yang terbentuk dari klausa FROM.

Demikian juga dengan fungsi IF, jika pada statemen SELECT terdapat
fungsi ini, maka fungsi ini akan dieksekusi sebanyak baris yang ada
pada tabel hasil klausa FROM

Agar lebih jelas, misal kita memiliki tabel buku_pinjam sebagai


berikut:
+-----------+---------+-------------+------------+-------------+
| id_pinjam | id_buku | id_peminjam | tgl_pinjam | tgl_kembali |
+-----------+---------+-------------+------------+-------------+
| 1 | 1 | 001 | 2017-05-13 | 0000-00-00 |
| 2 | 2 | 002 | 2017-05-10 | 0000-00-00 |

50 BAB 3 Ekspresi Logika


| 3 | 3 | 001 | 2017-05-12 | 2017-05-15 |
| 4 | 4 | 003 | 2017-05-14 | 0000-00-00 |
| 5 | 3 | 004 | 2017-05-12 | 2017-05-14 |
+-----------+---------+-------------+------------+-------------+

Selanjutnya kita akan menampilkan data buku beserta status


pengembalian dengan output tabel sebagai berikut:
+---------+------------+-------------+---------+
| id_buku | tgl_pinjam | tgl_kembali | Kembali |
+---------+------------+-------------+---------+
| 1 | 2017-05-13 | 0000-00-00 | Belum |
| 2 | 2017-05-10 | 0000-00-00 | Belum |
| 3 | 2017-05-12 | 2017-05-15 | Sudah |
| 4 | 2017-05-14 | 0000-00-00 | Belum |
| 3 | 2017-05-12 | 2017-05-14 | Sudah |
+---------+------------+-------------+---------+

Query yang kita jalankan adalah sebagai berikut:

1. SELECT id_buku
2. , tgl_pinjam
3. , tgl_kembali
4. , IF(tgl_kembali = "0000-00-00", "Belum", "Sudah")
AS Kembali
5. FROM buku_pinjam

Bagaimana alurnya?

Sesuai dengan yang telah kita pelajari, pertama MySQL akan memilih
tabel sesuai yang ada pada klausa FROM yaitu tabel buku_pinjam.

Selanjutnya MySQL akan mengeksekusi klausa SELECT dan


memeriksa apakah perlu membuat kolom baru. Jika ada kolom baru,
maka setiap baris tabel hasil klausa FROM akan dieksekusi untuk
mendapatkan nilai kolom baru tersebut.

Terakhir, MySQL akan menampilkan tabel output sesuai dengan


kolom yang didefinisikan pada klausa SELECT

Perhatikan ilustrasi berikut:

Killer Trik Query MySQL 51


Gambar 3.1. Ilustrasi Eksekusi Fungsi IF

3.2. Ekspresi CASE


Ekspresi Case memiliki fungsi sama dengan fungsi IF yaitu untuk
menguji suatu ekspresi, bedanya, case dapat digunakan untuk
menguji lebih dari satu ekspresi.

Dalam bahasa pemrograman umum, bentuk ekspresi case sama


seperti ini:

1. if (ekspresi) {
2. Hasil1
3. } elseif (ekspresi2) {

52 BAB 3 Ekspresi Logika


4. Hasil2
5. } elseif (ekspresi3) {
6. Hasil3
7. } else {
8. Hasil Else
9. }

Format penulisan ekspresi CASE adalah sebagai berikut:

1. SELECT kolom,
2. CASE
3. WHEN ekspresi1 THEN Hasil1
4. WHEN ekspresi2 THEN Hasil2
5. ...
6. [ELSE Hasil Else]
7. END
8. FROM nama_tabel

Banyaknya ekspresi yang dapat diuji oleh ekspresi CASE tidak


terbatas.

Pada query diatas, keyword CASE, WHEN, THEN, dan END wajib ada
sedangkan keyword ELSE bersifat opsional sehingga tidak wajib ada.

Contoh format ekspresi CASE dengan bentuk minimalis adalah


sebagai berikut:

1. SELECT kolom,
2. CASE WHEN ekspresi THEN Hasil END
3. FROM nama_tabel

Ekspresi Case ini powerfull untuk menguji kondisi yang kompleks


yang tidak bisa diselesaikan dengan fungsi IF atau ribet jika
menggunakan nested IF

Contoh ekspresi CASE dengan satu ekspresi: misal kita tampilkan


status peminjaman buku dengan kriteria sebagai berikut:

Killer Trik Query MySQL 53


 Jika belum kembali, status: Belum kembali

 Jika sudah kembali, status: Kembali dalam ... hari

Query yang kita jalankan adalah:

1. SELECT id_buku,
2. tgl_pinjam,
3. tgl_kembali,
4. CASE
5. WHEN tgl_kembali = "0000-00-00" THEN "Belum Kembali"
6. ELSE CONCAT("Kembali dalam "
7. , tgl_kembali - tgl_pinjam, " Hari")
8. END AS status
9. FROM buku_pinjam

Hasil:
+---------+------------+-------------+----------------------+
| id_buku | tgl_pinjam | tgl_kembali | status |
+---------+------------+-------------+----------------------+
| 1 | 2017-05-13 | 0000-00-00 | Belum Kembali |
| 2 | 2017-05-10 | 0000-00-00 | Belum Kembali |
| 3 | 2017-05-12 | 2017-05-15 | Kembali dalam 3 Hari |
| 4 | 2017-05-14 | 0000-00-00 | Belum Kembali |
| 3 | 2017-05-12 | 2017-05-14 | Kembali dalam 2 Hari |
+---------+------------+-------------+----------------------+

Karena hanya ada satu ekspresi, maka query diatas dapat diganti
dengan fungsi IF sebagai berikut:

1. SELECT id_buku, tgl_pinjam, tgl_kembali


2. , IF( tgl_kembali = "0000-00-00"
3. , "Belum Kembali"
4. , CONCAT("Kembali dalam "
5. , tgl_kembali - tgl_pinjam, " Hari"
6. )
7. ) AS status
8. FROM buku_pinjam

Contoh lebih dari satu ekspresi: kita akan menampilkan status stok
barang, misal kita memiliki tabel barang sebagai berikut:

54 BAB 3 Ekspresi Logika


+-----------+-------------+------+-------+
| kd_barang | nama_barang | stok | harga |
+-----------+-------------+------+-------+
| 1 | Mouse | 14 | 76000 |
| 2 | Flashdisk | 15 | 55000 |
| 3 | Mousepad | 17 | 35000 |
| 4 | Keyboard | 12 | 80000 |
| 5 | Kabel VGA | 7 | 45000 |
+-----------+-------------+------+-------+

Selanjutnya kita akan menampilkan nama barang, jumlah stoknya,


dan status nya dengan kriteria jika jumlah stok = 0, maka stok habis,
jika stok > 0 dan < 10 sedikit, stok >= 10 dan <= 15 cukup, selain itu
banyak. Query yang kita jalankan:

1. SELECT nama_barang,
2. stok,
3. CASE
4. WHEN stok = 0 THEN "Habis"
5. WHEN stok > 0 AND stok < 10 THEN "Sedikit"
6. WHEN stok >= 10 AND stok < 15 THEN "Cukup"
7. ELSE "Banyak"
8. END AS status_tok
9. FROM barang

Hasil yang kita peroleh:


+-------------+------+------------+
| nama_barang | stok | status_tok |
+-------------+------+------------+
| Mouse | 14 | Cukup |
| Flashdisk | 15 | Banyak |
| Mousepad | 17 | Banyak |
| Keyboard | 12 | Cukup |
| Kabel VGA | 7 | Sedikit |
+-------------+------+------------+

Killer Trik Query MySQL 55


Halaman ini sengaja dikosongkan
Jagowebdev.com

56 BAB 3 Ekspresi Logika


BAB 4 Menguasai JOIN
Pada MySQL dan RDBMS pada umumnya, kita dapat
menggabungkan dua atau lebih tabel menjadi satu, bentuk
penggabungan tersebut dapat berbentuk horizontal maupun
vertikal.

Bentuk horizontal diperoleh menggunakan JOIN, sedangkan bentuk


vertikal menggunakan UNION.

Pada bab II. Kunci Menguasai SELECT, telah disinggung sedikit


mengenal JOIN, pada bab ini, kita akan membahasnya lebih dalam.

Apa itu JOIN?

Seperti artinya yaitu gabungan, join dalam SQL berarti


menggabungkan tabel, bentuk penggabungannya adalah horizontal,
ilustrasinya adalah sebagai berikut:

Gambar 4. 1. Ilustrasi JOIN

Killer Trik Query MySQL 57


Pada MySQL, terdapat tiga jenis join yaitu JOIN, LEFT JOIN, dan RIGHT
JOIN

Join ini sangat powerful dan dalam model database relasional


(RDBMS), join menjadi alat yang ampuh untuk memecahkan
berbagai permasalahan query SQL yang melibatkan banyak tabel.
So…, kita harus benar benar menguasainya.

4.1. JOIN
Pada join, tabel digabungkan kemudian dihasilkan tabel baru yang
berisi data yang hanya ada pada kedua tabel.

Contoh kita memiliki tabel mhs dan mhs_jurusan sebagai berikut:


mhs
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |
| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
| 008 | Haris | L | NULL |
+------+---------+---------------+------------+

mhs_jurusan
+------------+--------------+
| kd_jurusan | nama_jurusan |
+------------+--------------+
| J001 | MANAJEMEN |
| J002 | AKUNTANSI |
| J003 | TEKNIK |
+------------+--------------+

Selanjutnya kita gabungkan kedua tabel tersebut menggunakan


INNER JOIN dengan query sebagai berikut:

58 BAB 4 Menguasai JOIN


1. SELECT *
2. FROM mhs
3. JOIN mhs_jurusan USING (kd_jurusan)

Hasil:
+------------+------+---------+---------------+--------------+
| kd_jurusan | nim | nama | jenis_kelamin | nama_jurusan |
+------------+------+---------+---------------+--------------+
| J002 | 001 | Alfa | L | AKUNTANSI |
| J002 | 002 | Beta | P | AKUNTANSI |
| J001 | 003 | Charlie | P | MANAJEMEN |
| J001 | 004 | Delta | L | MANAJEMEN |
| J001 | 005 | Erdhi | L | MANAJEMEN |
| J002 | 006 | Farah | P | AKUNTANSI |
| J002 | 007 | Gisel | P | AKUNTANSI |
+------------+------+---------+---------------+--------------+

Pada contoh diatas, kita menggabungkan data menggunakan data


kolom nim. Dari tabel yang dihasilkan, terlihat bahwa mahasiswa
dengan nama Haris tidak ditampilkan, karena kode jurusan nya yaitu
NULL tidak ada pada tabel mhs_jurusan. Demikian juga dengan
Jurusan teknik, jurusan ini tidak ditampilkan karena kode jurusan
J003 tidak ada pada tabel mhs

Note: Keyword JOIN dapat diganti dengan INNER JOIN


atau CROSS JOIN. Saya sendiri lebih memilih
menggunakan keyword JOIN saja karena lebih simpel.

4.2. USING atau ON


Pada contoh diatas, kondisi yang menghubungkan kedua tabel
kita definisikan menggunakan keyword USING, keyword ini
digunakan jika nama kolom yang menjadi penghubung ada pada
kedua tabel, yang pada contoh kali ini kolom kd_jurusan.

Killer Trik Query MySQL 59


Selain USING, kita juga dapat menggunakan keyword ON, misal
pada contoh diatas, jika menggunakan ON, maka klausa JOIN
akan berubah menjadi ON mhs.kd_jurusan = mhs_jurusan.kd_jurusan

Note: Saya pribadi sebisa mungkin menggunakan USING,


jika terpaksanya tidak bisa, baru menggunakan ON

4.3. LEFT JOIN


Pada left join, data pada tabel disebelah kiri klausa left join akan
ditampilkan semua, sedangkan data pada tabel sebelah kanan
ditampilkan hanya jika data tersebut ada pada tabel sebelah kiri

Jika data pada tabel sebelah kiri tidak memiliki pasangan data
dengan tabel di sebelah kanan, maka data pada tabel sebelah kanan
akan benilai NULL

Misal kita gabungkan tabel mhs dan mhs_jurusan dengan LEFT JOIN
sebagai berikut:

1. SELECT *
2. FROM mhs
3. LEFT JOIN mhs_jurusan USING (kd_jurusan)

Hasil yang kita peroleh:


+------------+------+---------+---------------+--------------+
| kd_jurusan | nim | nama | jenis_kelamin | nama_jurusan |
+------------+------+---------+---------------+--------------+
| J001 | 003 | Charlie | P | MANAJEMEN |
| J001 | 004 | Delta | L | MANAJEMEN |
| J001 | 005 | Erdhi | L | MANAJEMEN |
| J002 | 001 | Alfa | L | AKUNTANSI |
| J002 | 002 | Beta | P | AKUNTANSI |
| J002 | 006 | Farah | P | AKUNTANSI |
| J002 | 007 | Gisel | P | AKUNTANSI |
| NULL | 008 | Haris | L | NULL |
+------------+------+---------+---------------+--------------+

60 BAB 4 Menguasai JOIN


Dari contoh diatas terlihat bahwa MySQL menampilkan data
mahasiswa bernama Haris dan tidak menampilkan data jurusan
teknik.

Mengapa demikian?

Karena seperti yang telah disebutkan sebelumnya, pada LEFT JOIN


data pada tabel disebelah kiri, yaitu tabel mhs akan ditampilkan
semua, sedangkan data dengan kode jurusan J003 yang ada pada
tabel mhs_jurusan (tabel sebelah kanan) tidak ditampilkan karena
tidak memiliki pasangan dengan data pada tabel mhs.

Perhatikan bahwa karena Haris tidak memiliki data pada kolom


kd_jurusan, maka kolom nama_jurusan (kolom tabel sebelah kanan)
untuk Haris bernilai NULL

Note: Keyword LEFT JOIN dapat diganti dengan LEFT


OUTER JOIN. Saya sendiri lebih memilih menggunakan
keyword LEFT JOIN saja karena lebih simpel.

4.4. RIGHT JOIN


Kebalikan dari left join, pada right join, semua data pada tabel
disebelah kanan akan ditampilkan semua sedangkan data pada
tabel disebelah kiri ditampilkan hanya jika data tersebut ada pada
tabel disebelah kanan.

Jika data pada tabel sebelah kanan tidak memiliki pasangan dengan
tabel di sebelah kanan, maka data pada tabel sebelah kiri akan
benilai NULL

Sebagai contoh misal kita gabungkan tabel mhs dan mhs_jurusan


menggunakan RIGHT JOIN sebagai berikut:

Killer Trik Query MySQL 61


1. SELECT *
2. FROM mhs
3. RIGHT JOIN mhs_jurusan USING (kd_jurusan)

Hasil yang kita peroleh:


+------------+--------------+------+---------+---------------+
| kd_jurusan | nama_jurusan | nim | nama | jenis_kelamin |
+------------+--------------+------+---------+---------------+
| J002 | AKUNTANSI | 001 | Alfa | L |
| J002 | AKUNTANSI | 002 | Beta | P |
| J001 | MANAJEMEN | 003 | Charlie | P |
| J001 | MANAJEMEN | 004 | Delta | L |
| J001 | MANAJEMEN | 005 | Erdhi | L |
| J002 | AKUNTANSI | 006 | Farah | P |
| J002 | AKUNTANSI | 007 | Gisel | P |
| J003 | TEKNIK | NULL | NULL | NULL |
+------------+--------------+------+---------+---------------+

Pada contoh diatas terlihat bahwa data pada tabel mhs_jurusan


(tabel disebelah kanan) akan ditampilkan semua sedangkan pada
tabel sebelah kiri yaitu tabel mhs data tanpa kode jurusan tidak
ditampilkan, karena tidak memiliki pasangan dengan data pada tabel
mhs

Perhatikan sama seperti sebelumnya, karena jurusan Teknik tidak


memiliki mahasiswa, maka kolom nim, nama, dan jenis_kelamin
(kolom tabel sebelah kiri) untuk jurusan Teknik bernilai NULL

Tips: kita cukup menghafal JOIN dan LEFT JOIN saja.


Kenapa? Karena RIGHT JOIN ini secara mudah dapat
diganti dengan LEFT JOIN yaitu hanya dengan mengubah
posisi tabel nya, misal pada contoh diatas kita ubah posisi
tabel menjadi FROM mhs_jurusan LEFT JOIN mhs, hasil yang kita
peroleh akan sama, sehingga untuk mempermudah
pemahaman, sebaiknya cukup menggunakan LEFT JOIN
saja.

62 BAB 4 Menguasai JOIN


4.5. Hal Penting Tentang JOIN
Sebagaimana yang telah kita pelajari, JOIN, LEFT JOIN, dan RIGHT
JOIN, ketiganya minimal akan menampilkan data yang cocok pada
kedua tabel, sebagai tambahan, LEFT JOIN menampilkan semua
data tabel disebelah kiri, dan RIGHT JOIN semua data tabel
disebelah kanan

Dengan demikian dapat disimpulkan bahwa pada intinya join


akan selalu menampilkan data yang cocok pada kedua tabel

Pada kondisi tertentu mungkin Anda akan terkejut dengan hasil


yang diperoleh, misal kita memiliki tabel barang_masuk dan
barang_keluar sebagai berikut:
barang_masuk
+------------+-----------+-------------+-----------+
| tgl_masuk | kd_barang | kd_supplier | jml_masuk |
+------------+-----------+-------------+-----------+
| 2017-05-02 | 1 | 1 | 14 |
| 2017-05-03 | 1 | 2 | 5 |
| 2017-04-26 | 1 | 1 | 7 |
+------------+-----------+-------------+-----------+
barang_keluar
+------------+-----------+------------+
| tgl_keluar | kd_barang | jml_keluar |
+------------+-----------+------------+
| 2017-05-11 | 1 | 13 |
| 2017-05-12 | 1 | 4 |
| 2017-04-27 | 1 | 5 |
+------------+-----------+------------+

Jika kita lakukan join, baik JOIN, LEFT JOIN maupun RIGHT JOIN,
data berikut ini akan muncul
+-----------+------------+-----------+------------+------------+
| kd_barang | tgl_masuk | jml_masuk | tgl_keluar | jml_keluar |
+-----------+------------+-----------+------------+------------+
| 1 | 2017-05-02 | 14 | 2017-05-11 | 13 |
| 1 | 2017-05-03 | 5 | 2017-05-11 | 13 |
| 1 | 2017-04-26 | 7 | 2017-05-11 | 13 |

Killer Trik Query MySQL 63


| 1 | 2017-05-02 | 14 | 2017-05-12 | 4 |
| 1 | 2017-05-03 | 5 | 2017-05-12 | 4 |
| 1 | 2017-04-26 | 7 | 2017-05-12 | 4 |
| 1 | 2017-05-02 | 14 | 2017-04-27 | 5 |
| 1 | 2017-05-03 | 5 | 2017-04-27 | 5 |
| 1 | 2017-04-26 | 7 | 2017-04-27 | 5 |
+-----------+------------+-----------+------------+------------+

Kenapa seperti itu? Seperti yang telah kita bahas, pada join,
semua data yang ada pada kedua tabel (termasuk kombinasinya)
akan ditampilkan. Perhatikan ilustrasi berikut:

Gambar 4.2 Ilustrasi Data Yang Ditampilkan Oleh Klausa JOIN

Dengan memahami karakteristik tersebut, maka akan


memudahkan kita ketika memecahkan kasus yang melibatkan
banyak tabel.

Hasil tersebut diatas umumnya terjadi jika hubungan antar kedua


tabel bersifat many to many yang artinya setiap data pada masing
masing tabel memiliki hubungan lebih dari satu data pada tabel
lain.

64 BAB 4 Menguasai JOIN


Ciri hubungan tersebut adalah nilai pada kolom yang
berhubungan (pada contoh diatas kolom kd_barang) memiliki
nilai yang sama lebih dari satu pada kedua tabel, pada contoh
diatas, nilai kd_barang pada tabel barang_masuk maupun
barang_keluar memiiki nilai yang sama (nilai 1) lebih dari satu.

Killer Trik Query MySQL 65


Halaman ini sengaja dikosongkan
Jagowebdev.com

66 BAB 4 Menguasai JOIN


BAB 5 Menguasai Fungsi Agregasi
Pada bab awal telah disinggung sedikit mengenai fungsi agregasi,
yaitu fungsi SUM dan COUNT, pada bab ini kita akan membahasnya
lebih dalam.

Apa itu fungsi agregasi?

Fungsi agregasi adalah fungsi yang dalam operasinya melibatkan


seluruh baris pada suatu kolom, kenapa demikian? karena seperti
namanya, agergat yang artinya mengumpulkan sesuatu (dalam hal
ini nilai) menjadi satu.

Fungsi agregasi yang sering digunakan adalah SUM, COUNT, MIN,


MAX, dan AVG, kita akan bahas satu per beberapa sesaat kedepan.

5.1. Memahami Cara Kerja Fungsi


Agragasi
Seperti yang telah disebutkan sebelumnya, fungsi agregasi akan
menggabungkan semua data (baris) pada suatu kolom menjadi satu,
jika terdapat klausa GROUP BY, maka data tersebut akan
dikelompokkan berdasarkan data yang sama pada kolom yang ada
pada klausa GROUP BY.

Jangan lupa untuk selalu menggunakan GROUP BY ketika


menggunakan fungsi agregasi kecuali jika ingin
menggabungkan semua data.

Sebagai contoh: mengulang contoh pada bab sebelumnya, misal kita


memiliki tabel penjualan sebagai berikut:

Killer Trik Query MySQL 67


+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Selanjutnya kita tampilkan data id_pelanggan beserta jumlah


transaksinya di tahun 2017, query yang kita jalankan:

1. SELECT id_pelanggan, COUNT(id_trx), SUM(total_trx)


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. GROUP BY id_pelanggan

Hasilnya adalah sebagai berikut:


+--------------+---------------+----------------+
| id_pelanggan | COUNT(id_trx) | SUM(total_trx) |
+--------------+---------------+----------------+
| 0 | 1 | 259000 |
| 1 | 2 | 378000 |
| 2 | 1 | 110000 |
+--------------+---------------+----------------+

Bagaimana proses fungsi eksekusi agregasinya?

Pertama tama MySQL akan mengeksekusi klausa FROM dan


diperoleh tabel penjualan

Selanjutnya, MySQL akan mengeksekusi klausa SELECT. Karena


terdapat fungsi agregasi COUNT dan SUM, maka kedua fungsi
tersebut akan dieksekusi bersama sama dengan klausa GROUP BY

Selanjutnya, pada klausa GROUP BY, data akan di kelompokkan


berdasarkan kolom id_pelanggan (kolom yang ada pada klausa
GROUP BY) kemudian MySQL mengeksekusi eksekusi fungsi COUNT
dan SUM.

68 BAB 5 Menguasai Fungsi Agregasi


Terakhir, MySQL akan menampilkan tabel output dengan kolom
sesuai dengan yang didefinisikan pada klausa SELECT

Ilustrasinya adalah sebagai berikut:

Gamber 5.1. Ilustrasi Alur Query

Perhatikan bahwa pada contoh diatas, ketika menjalankan GROUP


BY dan sebelum menjalankan fungsi agregasi COUNT dan SUM,
tabel hasil klausa FROM akan diurutkan terlebih dahulu secara
ascending berdasarkan kolom pada klausa GROUP BY yaitu kolom
id_pelanggan.

Killer Trik Query MySQL 69


5.2. MAX, MIN, dan AVG
MAX dan MIN
Fungsi MAX digunakan untuk mengambil nilai tertinggi suatu kolom,
sedangkan MIN untuk mengambil nilai terendah. Misal kita memiliki
tabel barang sebagai berikut:
+-----------+-------------+------+-------+
| id_barang | nama_barang | stok | harga |
+-----------+-------------+------+-------+
| 1 | Mouse | 14 | 76000 |
| 2 | Flashdisk | 15 | 55000 |
| 3 | Mousepad | 17 | 35000 |
| 4 | Keyboard | 12 | 80000 |
| 5 | Kabel VGA | 5 | 45000 |
+-----------+-------------+------+-------+

Selanjutnya kita akan menampilkan barang yang memiliki stok paling


banyak dan stok paling sedikit. Query yang kita jalankan

1. SELECT MIN(stok), MAX(stok)


2. FROM barang

Hasil yang kita peroleh:


+-----------+-----------+
| MIN(stok) | MAX(stok) |
+-----------+-----------+
| 5 | 17 |
+-----------+-----------+

Penting diperhatikan bahwa ketika menggunakan fungsi


agregasi, maka kita tidak dapat menyandingkan data pada
kolom agregasi dengan data pada kolom non agregasi jika
kolom non agregasi memiliki data yang tidak sama, atau tidak
terdapat klausa GROUP BY

Perhatikan contoh berikut:

70 BAB 5 Menguasai Fungsi Agregasi


1. SELECT nama_barang, MIN(stok), stok
2. FROM barang

Hasil:
+-------------+-----------+------+
| nama_barang | MIN(stok) | stok |
+-------------+-----------+------+
| Mouse | 7 | 14 |
+-------------+-----------+------+

Pada contoh diatas, seharusnya stok paling kecil adalah Kabel VGA,
bukan mouse. Hal ini terjadi karena ketika menjalankan fungsi
aregasi, MySQL akan menggbungkan semua data kolom agregasi
(kolom stok) dan mengambil baris paling atas untuk kolom non
agregasi (kolom nama_barang dan stok)

Pada contoh diatas kolom nama_barang dan stok (kolom paling


kanan), data yang diambil adalah data pada baris paling atas yaitu
Mouse dan 14.

Sebagai "rule of thumb" jika ingin menggunakan kolom non


agregasi, pastikan data pada semua baris pada kolom non
agregasi isinya sama, atau pastikan kolom tersebut
tercantum pada klausa GROUP BY

Lanjutan…
Mari kita lanjutkan… berdasarkan pola diatas, untuk mencari nilai
minimal dan maksimal, kita harus menggunakan cara lain, contoh
query untuk mencari barang dengan stok terkecil:

1. SELECT nama_barang, stok


2. FROM barang
3. ORDER BY stok
4. LIMIT 1

Killer Trik Query MySQL 71


Hasil:
+-------------+------+
| nama_barang | stok |
+-------------+------+
| Kabel VGA | 7 |
+-------------+------+

Pada contoh diatas, kita urutkan data berdasarkan stok terkecil,


kemudian kita ambil 1 baris teratas.

Sekarang, Anda coba buat query untuk mendapatkan barang


dengan nilai stok terbesar… bisa kan?

AVG
AVG digunakan untuk mendapatkan nilai rata-rata dari suatu kolom,
misal untuk menghitung rata-rata jumlah stok, kita gunakan query
berikut:

1. SELECT AVG(stok)
2. FROM barang

Hasil:
+-----------+
| AVG(stok) |
+-----------+
| 13.0000 |
+-----------+

Karena nilai AVG diperoleh dari hasil pembagian (total nilai


baris/banyaknya baris), maka hasil bagi tersebut kemungkinan
besar tidak bulat (ada pecahan) sehingga agar penghitungan
lebih akurat, hasil fungsi AVG akan menyertakan 4 digit
desimal, pada contoh diatas: 14 + 15 + 17 + 12 + 7 / 5 =
13.0000

72 BAB 5 Menguasai Fungsi Agregasi


Untuk membatasi banyaknya digit dibelakang koma, kita gunakan
fungsi ROUND, misal kita bulatkan hasil diatas dengan dua digit
dibelaang koma:

1. SELECT AVG(stok)
2. FROM barang

Hasil:
+-----------------------+
| ROUND( AVG(stok), 2 ) |
+-----------------------+
| 13.00 |
+-----------------------+

5.3. COUNT dan SUM


Pada bagian sebelumnya, kita sudah membahas sekilas mengenai
kedua fungsi ini, pada bagian ini kita akan membahasnya lebih
dalam.

Pada MySQL, fungsi COUNT digunakan untuk menghitung


banyaknya baris tidak termasuk baris yang memiliki nilai NULL,
sedangkan SUM digunakan untuk menjumlahkan nilai semua baris.

Ingat selalu bahwa fungsi COUNT tidak menghitung baris


yang memiliki nulai NULL

Sebagai contoh: kembali mengulang contoh sebelumnya, misal kita


memilliki tabel penjualan dengan data sebagai berikut:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-02-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 1 | 2017-04-10 | 259000 |
| 4 | 2 | 2016-12-02 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Killer Trik Query MySQL 73


Selanjutnya kita tampilkan banyaknya penjualan dan jumlah
penjualan untuk tahun 2017, query yang kita jalankan:

1. SELECT COUNT(id_trx) AS jml_trx,


2. SUM(total_trx) AS total
3. FROM penjualan
4. WHERE YEAR(tgl_trx) = 2017

Hasil:
+---------+--------+
| jml_trx | total |
+---------+--------+
| 3 | 637000 |
+---------+--------+

Selanjutnya, kita tampilkan data jumlah transaksi dan total nilai


transaksi yang dikelompokkan per tahun. Query yang kita jalankan:

1. SELECT YEAR(tgl_trx) AS thn_transaksi


2. , COUNT(id_trx) AS jml_trx
3. , SUM(total_trx) AS total
4. FROM penjualan
5. GROUP BY YEAR(tgl_trx)

Hasil yang kita peroleh:


+---------------+---------+--------+
| thn_transaksi | jml_trx | total |
+---------------+---------+--------+
| 2016 | 2 | 366000 |
| 2017 | 3 | 637000 |
+---------------+---------+--------+

COUNT dan SUM adalah fungsi agregasi yang sangat sering


digunakan, untuk itu selalu jadikan prioritas ketika
menghadapi berbagai permasalahan SQL, ingat selalu:
HITUNG ? ya COUNT, JUMLAH ? ya SUM

74 BAB 5 Menguasai Fungsi Agregasi


5.4. SUM Dengan IF
Pada pembahasan diawal, kita telah sedikit menyinggung
penggunaan fungsi IF didalam fungsi agregasi COUNT. Kali ini kita
akan merefresh kembali pemahaman kita dengan
menggabungkannya dengan fungsi COUNT

Contoh kita akan menghitung jumlah penjualan dan total nilai


penjualan untuk tahun 2017 per item. Output tabel yang diinginkan
adalah sebagai berikut:
+-------------+------+-------------+-----------+--------+----------+
| nama_barang | stok | jml_terjual | jml_bruto | diskon | jml_neto |
+-------------+------+-------------+-----------+--------+----------+
| Mouse | 14 | 2 | 146000 | 0 | 146000 |
| Flashdisk | 15 | 1 | 110000 | 0 | 110000 |
| Mousepad | 17 | 2 | 95000 | 0 | 95000 |
| Keyboard | 12 | 1 | 160000 | 16000 | 144000 |
| Kabel VGA | 7 | 2 | 135000 | 9000 | 126000 |
+-------------+------+-------------+-----------+--------+----------+

Adapun tabel yang tersedia adalah sebagai berikut:


barang
+-----------+-------------+------+-------+
| id_barang | nama_barang | stok | harga |
+-----------+-------------+------+-------+
| 1 | Mouse | 14 | 76000 |
| 2 | Flashdisk | 15 | 55000 |
| 3 | Mousepad | 17 | 35000 |
| 4 | Keyboard | 12 | 80000 |
| 5 | Kabel VGA | 7 | 45000 |
+-----------+-------------+------+-------+
penjualan_detail
+----+--------+-----------+------+--------------+--------+
| id | id_trx | id_barang | qty | harga_satuan | diskon |
+----+--------+-----------+------+--------------+--------+
| 1 | 1 | 1 | 1 | 76000 | 0 |
| 2 | 1 | 3 | 1 | 35000 | 0 |
| 3 | 1 | 5 | 2 | 45000 | 0.1 |
| 4 | 2 | 1 | 1 | 76000 | 0 |
| 5 | 2 | 2 | 2 | 55000 | 0 |
| 6 | 3 | 3 | 2 | 35000 | 0 |
| 7 | 3 | 5 | 1 | 45000 | 0 |

Killer Trik Query MySQL 75


| 8 | 3 | 4 | 2 | 80000 | 0.1 |
| 9 | 4 | 2 | 2 | 55000 | 0 |
| 10 | 5 | 4 | 4 | 80000 | 0.2 |
+----+--------+-----------+------+--------------+--------+
penjualan
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-02-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 1 | 2017-04-10 | 259000 |
| 4 | 2 | 2016-12-02 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Keterangan: tabel penjualan detail merupakan rincian dari tiap item


pada tabel penjualan.

Bagaimana query SQL nya ?

STOP !!!

Sebelum melanjutkan, silakan Anda coba menyelesaikan dengan


step by step cara yang telah kita bahas pada bagian Kunci
Memahami SELECT.

Practice make perfect… right?

Oke, jika sudah, mari kita bahas query nya ya…

Pertama…
Seperti biasa rule pertama yang akan kita lakukan adalah
mendefinisikan bentuk tabel hasil dari klausa FROM dengan WHERE
(jika ada), untuk dapat melakukannya kita perlu untuk menganalisa
tabel output untuk menentukan tabel mana yang akan digunakan:

 Pada tabel output terdapat kolom nama barang dan stok yang
kita dapatkan dari tabel barang.

76 BAB 5 Menguasai Fungsi Agregasi


 Kolom jml_terjual, jml_bruto, diskon, dan neto yang kita peroleh
dari tabel penjualan_detail

 Terdapat ketentuan bahwa yang data yang akan kita tampilkan


adalah data tahun 2017. Data tahun ini kita peroleh dari data
tanggal penjualan yang ada pada tabel penjualan

Dari hasil identifikasi diatas, maka tabel hasil klausa FROM harus
melibatkan tabel barang, penjualan, dan penjualan_detail untuk itu
kita harus menggabungkan ketiganya.

Bagaimana model penggabungannya? JOIN atau UNION?

Karena tabel output terdiri dari kolom yang ada pada tabel barang
dan penjualan_detail, maka kita gunakan penggabungan horizontal
yaitu menggunakan JOIN, selain itu ketiganya tabel tersebut juga
saling berhubungan

Selanjutnya, bagaimana urutan tabel nya? Mana tabel yang kita


letakkan disebelah kiri?

Urutan tabel dari yang pailng kiri adalah tabel barang, kenapa ? Ingat
pada pembahasan tentang LEFT JOIN, agar data ditampilkan semua,
tempatkan tabel disebelah kiri.

Karena kali ini tujuan kita menampilkan data semua barang beserta
data penjualannya, maka tujuan utama adalah menampilkan data
barang, sehingga kita letakkan tabel barang disebelah kiri.

Selanjutnya, kita lihat bentuk tabel hasil klausa FROM dengan


menggabungkan ketiga tabel tersebut menggunakan klausa SELECT
* . Jalankan query berikut:

1. SELECT *

Killer Trik Query MySQL 77


2. FROM barang
3. LEFT JOIN penjualan_detail USING(kd_barang)
4. LEFT JOIN penjualan USING (id_trx)
5. WHERE YEAR(tgl_trx) = 2017

Hasilya adalah:
+--------+-----------+-------------+------+-------+------+------+--------------+
| id_trx | id_barang | nama_barang | stok | harga | id | qty | harga_satuan |
+--------+-----------+-------------+------+-------+------+------+--------------+
| 1 | 1 | Mouse | 14 | 76000 | 1 | 1 | 76000 |
| 1 | 3 | Mousepad | 17 | 35000 | 2 | 1 | 35000 |
| 1 | 5 | Kabel VGA | 7 | 45000 | 3 | 2 | 45000 |
| 2 | 1 | Mouse | 14 | 76000 | 4 | 1 | 76000 |
| 2 | 2 | Flashdisk | 15 | 55000 | 5 | 2 | 55000 |
| 3 | 3 | Mousepad | 17 | 35000 | 6 | 2 | 35000 |
| 3 | 5 | Kabel VGA | 7 | 45000 | 7 | 1 | 45000 |
| 3 | 4 | Keyboard | 12 | 80000 | 8 | 2 | 80000 |
| 4 | 2 | Flashdisk | 15 | 55000 | 9 | 2 | 55000 |
| 5 | 4 | Keyboard | 12 | 80000 | 10 | 4 | 80000 |
+--------+-----------+-------------+------+-------+------+------+--------------+
Lanjutan...
+--------+--------------+------------+-----------+
| diskon | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 0 | 1 | 2017-02-02 | 192000 |
| 0 | 1 | 2017-02-02 | 192000 |
| 0.1 | 1 | 2017-02-02 | 192000 |
| 0 | 1 | 2017-03-10 | 186000 |
| 0 | 1 | 2017-03-10 | 186000 |
| 0 | 1 | 2017-04-10 | 259000 |
| 0 | 1 | 2017-04-10 | 259000 |
| 0.1 | 1 | 2017-04-10 | 259000 |
| 0 | 2 | 2016-12-02 | 110000 |
| 0.2 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Dari tabel diatas sudah kebayangkan bagaimana nantinya klausa


SELECT yang akan kita jalankan?

Yup, mari kita cari jawabaannya…

Kedua…
Langkah selanjutnya kita susun klausa SELECT

78 BAB 5 Menguasai Fungsi Agregasi


Ingat apakah kita akan mengelompokkan data? Jika ya
gunakan fungsi agregasi dan klausa GROUP BY

Selanjutnya, berdasarkan tabel hasil klausa FROM, kita identifikasi


kolom pada klausa SELECT:

1. nama_barang. Kolom nama barang sudah ada sehingga tinggal


kita masukkan dalam klausa SELECT

2. stok. Kolom stok juga sudah tersedia

3. jml_terjual. Untuk mendapatkan angka jumlah yang terjual, kita


jumlahkan nilai pada kolom qty, untuk itu kita gunakan fungsi
SUM(qty)

4. jml_bruto. Jumlah penjualan bruto diperoleh dengan


menjumlahkan (SUM) semua nilai dari hasil perkalian antara qty
dengan harga_satuan sehingga kita gunakan SUM(qty *
harga_satuan)

5. diskon. Nilai diskon diperoleh dari penjumlahan semua baris dari


hasil perkalian qty * harga_satuan * diskon, fungsi yang kkita
gunakan SUM(qty * harga_satual * diskon)

6. jml_neto diperoleh dengan mengurangkan nilai pada angka 4


dengan angka 5.

Selanjutnya apakah kita perlu mengelompokkan data?

Pada contoh kali ini kita akan menampilkan data per item barang,
sehingga data perlu dikelompokkan berdasarkan kd_barang
(GROUP BY kd_barang)

Hasil bentuk querynya adalah sebagai berikut:

Killer Trik Query MySQL 79


1. SELECT nama_barang
2. , stok
3. , SUM(qty) AS jml_terjual
4. , SUM(qty * harga_satuan) AS jml_bruto
5. , ROUND(SUM(qty * harga_satuan * diskon)) AS diskon
6. , ROUND ( SUM(qty * harga_satuan)
7. - SUM(qty * harga_satuan * diskon)
8. ) AS jml_neto
9. FROM barang
10. LEFT JOIN penjualan_detail USING(id_barang)
11. LEFT JOIN penjualan USING(id_trx)
12. WHERE YEAR(tgl_trx) = 2017
13. GROUP BY id_barang

Hasil:
+-------------+------+-------------+-----------+--------+----------+
| nama_barang | stok | jml_terjual | jml_bruto | diskon | jml_neto |
+-------------+------+-------------+-----------+--------+----------+
| Mouse | 14 | 2 | 146000 | 0 | 146000 |
| Flashdisk | 15 | 2 | 110000 | 0 | 110000 |
| Mousepad | 17 | 3 | 95000 | 0 | 95000 |
| Keyboard | 12 | 2 | 160000 | 16000 | 144000 |
| Kabel VGA | 7 | 3 | 135000 | 9000 | 126000 |
+-------------+------+-------------+-----------+--------+----------+

Selanjutnya coba Anda tambahkan query sehingga muncul baris


TOTAL di bagian paling bawan. Bisa kan?

5.5. COUNT Dengan CASE


Selain fungsi IF, dalam praktik, kita sering menemukan
penggabungan fungsi agregasi (COUNT dan SUM) dengan ekspresi
CASE dengan bentuk seperti berikut ini:

1. SELECT
2. COUNT(
3. CASE WHEN ... THEN ...
4. END
5. )
6. FROM ...

80 BAB 5 Menguasai Fungsi Agregasi


Bagian ini kita akan mengasah pemahaman anda tentang
penggabungan kedua fungsi tersebut

Sebagai contoh isal kita memiliki tabel dosen dan dosen_absen


dengan data sebagai berikut:
dosen
+----------+------------+
| id_dosen | nama_dosen |
+----------+------------+
| 1 | Abrar |
| 2 | Bahrun |
| 3 | Choirul |
| 4 | Dirman |
| 5 | Endri |
+----------+------------+
dosen_absen
+----------+------------+------------+
| id_dosen | tanggal | keterangan |
+----------+------------+------------+
| 3 | 2017-08-13 | I |
| 2 | 2016-12-10 | S |
| 3 | 2017-05-13 | A |
| 2 | 2017-03-02 | S |
| 2 | 2017-05-13 | S |
+----------+------------+------------+

Selanjutnya kita akan menampilkan rekap data ketidakhadiran


dosen per semester dengan output sebagai berikut:
+-------+----------+------------+------------+------------+
| tahun | id_dosen | nama_dosen | semester_1 | semester_2 |
+-------+----------+------------+------------+------------+
| 2016 | 2 | Bahrun | 0 | 1 |
| 2017 | 2 | Bahrun | 2 | 0 |
| 2017 | 3 | Choirul | 1 | 1 |
+-------+----------+------------+------------+------------+

Bagaimana Querynya?

Dicoba dulu ya?

Alurnya sama persis dengan bagian sebelumnya: SUM dengan IF

Killer Trik Query MySQL 81


Sudah?

Baiklah, query versi saya adalah:

1. SELECT YEAR(tanggal) AS tahun,


2. id_dosen,
3. nama_dosen,
4. COUNT(
5. CASE WHEN MONTH(tanggal) >= 1 AND MONTH(tanggal) <= 6
6. THEN tanggal
7. ELSE NULL
8. END
9. ) AS semester_1,
10. COUNT(
11. CASE WHEN MONTH(tanggal) >= 7 AND MONTH(tanggal) <= 12
12. THEN tanggal
13. ELSE NULL
14. END
14. ) AS semester_2
16. FROM dosen_absen
17. LEFT JOIN dosen USING(id_dosen)
18. GROUP BY id_dosen, tahun

Pada query diatas, kita menggunakan fungsi agregasi COUNT untuk


menghitung jumlah baris sesuai dengan tanggal absen, apakah
masuk semester 1 (bulan Januari s.d Juni) atau semester 2 (bulan Juli
s.d Desember).

Dengan cara yang sama, kita juga dapat menghitung jumlah


ketidakhadiran per triwulan.

1. SELECT YEAR(tanggal) AS tahun,


2. id_dosen,
3. nama_dosen,
4. COUNT(
5. CASE WHEN MONTH(tanggal) >= 1 AND MONTH(tanggal) <= 3
6. THEN tanggal
7. ELSE NULL
8. END
9. ) AS triwulan_1,
10. COUNT(
11. CASE WHEN MONTH(tanggal) >= 4 AND MONTH(tanggal) <= 6

82 BAB 5 Menguasai Fungsi Agregasi


12. THEN tanggal
13. ELSE NULL
14. END
14. ) AS triwulan_2,
16. COUNT(
17. CASE WHEN MONTH(tanggal) >= 7 AND MONTH(tanggal) <= 9
18. THEN tanggal
19. ELSE NULL
20. END
21. ) AS triwulan_3,
22. COUNT(
23. CASE WHEN MONTH(tanggal) >= 10 AND MONTH(tanggal) <= 12
24. THEN tanggal
25. ELSE NULL
26. END
27. ) AS triwulan_4
28. FROM dosen_absen
29. LEFT JOIN dosen USING(id_dosen)
30. GROUP BY id_dosen, tahun

Hasil yang kita peroleh:


+-------+----------+------------+------------+------------+------------+------------+
| tahun | id_dosen | nama_dosen | triwulan_1 | triwulan_2 | triwulan_3 | triwulan_4 |
+-------+----------+------------+------------+------------+------------+------------+
| 2016 | 2 | Bahrun | 0 | 0 | 0 | 1 |
| 2017 | 2 | Bahrun | 1 | 1 | 0 | 0 |
| 2017 | 3 | Choirul | 0 | 1 | 1 | 0 |
+-------+----------+------------+------------+------------+------------+------------+

Jika kita ingin menambahkan baris total, kita cukup tambahkan


klausa WITH ROLLUP di bagian paling bawah:

30. GROUP BY id_dosen, tahun


31. WITH ROLLUP

Hasil:
+-------+----------+------------+------------+------------+------------+------------+
| tahun | id_dosen | nama_dosen | triwulan_1 | triwulan_2 | triwulan_3 | triwulan_4 |
+-------+----------+------------+------------+------------+------------+------------+
| 2016 | 2 | Bahrun | 0 | 0 | 0 | 1 |
| 2017 | 2 | Bahrun | 1 | 1 | 0 | 0 |
| NULL | 2 | Bahrun | 1 | 1 | 0 | 1 |
| 2017 | 3 | Choirul | 0 | 1 | 1 | 0 |
| NULL | 3 | Choirul | 0 | 1 | 1 | 0 |
| NULL | NULL | Choirul | 1 | 2 | 1 | 1 |
+-------+----------+------------+------------+------------+------------+------------+

Killer Trik Query MySQL 83


Lebih jauh tentang klausa WITH ROLLUP dapat dibaca pada BAB 11.

5.6. Penggabungan Fungsi Agregasi


Dengan Ekspresi Logika
Pada bagian sebelumnya kita telah beberapa kali menggunakan
fungsi agregasi (baik COUNT dan SUM) bersama dengan ekspresi
logika (fungsi IF dan ekspresi CASE), dan sudah dibahas juga
bagaimana alur querynya ketika menggunakan kedua fungsi
tersebut secara bersamaan.

Dalam praktik, kita akan sering menemui bentuk seperti ini (fungsi
agregasi digabung dengan ekspresi logika), untuk itu, kita perlu
menguasainya dengan baik, terutama alur querynya, nah, untuk itu,
pada bagian ini kita perdalam lagi bagaimana MySQL menangani
kedua fungsi tersebut secara bersamaan .

Penggabungan fungsi agregasi dengan ekspresi logika dilakukan


dalam bentuk nested function (fungsi di dalam fungsi) dan biasanya
ekspresi logika berada di dalam, sedangkan fungsi agregasi berada
diluar

Pada nested function, MySQL terlebih dahulu akan mengeksekusi


fungsi yang ada di dalam (ekspresi logika) baru kemudian fungsi yang
ada di luar, dalam hal ini fungsi agregasi

Misal jika kita menjalankan fungsi COUNT(IF(YEAR(tanggal) = 2017, 1,


NULL)) AS status maka urutan fungsi yang dieksekusi adalah YEAR,
IF, baru kemudian COUNT

84 BAB 5 Menguasai Fungsi Agregasi


COUNT dan IF
Untuk mempermudah pemahaman, misal kita akan membuat rekap
jumlah mahasiswa yang lulus dan tidak lulus yang dikelompokkan
berdasarkan jenis kelamin dengan tabel output sebagai berikut:
+-------------+---+---+
| status | L | P |
+-------------+---+---+
| LULUS | 2 | 2 |
| TIDAK LULUS | 1 | 2 |
+-------------+---+---+

Tabel yang kita gunakan adalah tabel mhs dan mhs_status dengan
data sebagai berikut:
mhs
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |
| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
+------+---------+---------------+------------+
mhs_status
+------+-------------+
| nim | status |
+------+-------------+
| 001 | LULUS |
| 002 | TIDAK LULUS |
| 003 | TIDAK LULUS |
| 004 | LULUS |
| 005 | TIDAK LULUS |
| 006 | LULUS |
| 007 | LULUS |
+------+-------------+

Bagaimana querynya?

Killer Trik Query MySQL 85


Pertama…
Pertama kita definisikan tabel pada klausa FROM, karena kita
memerlukan kedua tabel, maka kita perlu menggabungkan
keduanya, kali ini kita akan menggunakan join dan kita letakkan tabel
mhs_status disebelah kiri, kenapa? Karena kita akan menampilkan
semua status lulus dan tidak lulus

Mari kita tes hasil penggabungan nya, jalankan query berikut:

1. SELECT *
2. FROM mhs_status
3. LEFT JOIN mhs USING (nim);

Hasil:
+------+-------------+---------+---------------+------------+
| nim | status | nama | jenis_kelamin | kd_jurusan |
+------+-------------+---------+---------------+------------+
| 001 | LULUS | Alfa | L | J002 |
| 002 | TIDAK LULUS | Beta | P | J002 |
| 003 | TIDAK LULUS | Charlie | P | J001 |
| 004 | LULUS | Delta | L | J001 |
| 005 | TIDAK LULUS | Erdhi | L | J001 |
| 006 | LULUS | Farah | P | J002 |
| 007 | LULUS | Gisel | P | J002 |
+------+-------------+---------+---------------+------------+

Dari hasil diatas sudah kebayang kan bagaimana selanjutnya? Yup,


mari kita lanjutkan

Kedua…
Selanjutnya kita definisikan klausa SELECT. Pada tabel output
terdapat kolom baru yaitu L dan P, untuk itu, kita perlu
menambahkannya pada klausa SELECT

Data pada kolom tersebut berisi jenis kelamin dari mahasiswa, untuk
itu jika jenis kelamin sesuai dengan kolom, maka kita beri nilai 1 jika

86 BAB 5 Menguasai Fungsi Agregasi


tidak maka kita beri nilai NULL, misal pada siswa dengan jenis
kelamin L maka kolom L akan bernilai 1 dan kolom P NULL, kenapa
1 dan NULL? Karena kita akan menghitung baris menggunakan
fungsi COUNT dan agar baris tersebut tidak ikut dihitung mau tidak
mau kita harus menggunakan NULL

Bentuk tabel hasil eksekusi klausa SELECT yang kita inginkan adalah
sebagai berikut:
+------+-------------+---------+---------------+------------+------+------+
| nim | status | nama | jenis_kelamin | kd_jurusan | L | P |
+------+-------------+---------+---------------+------------+------+------+
| 001 | LULUS | Alfa | L | J002 | 1 | NULL |
| 002 | TIDAK LULUS | Beta | P | J002 | NULL | 1 |
| 003 | TIDAK LULUS | Charlie | P | J001 | NULL | 1 |
| 004 | LULUS | Delta | L | J001 | 1 | NULL |
| 005 | TIDAK LULUS | Erdhi | L | J001 | 1 | NULL |
| 006 | LULUS | Farah | P | J002 | NULL | 1 |
| 007 | LULUS | Gisel | P | J002 | NULL | 1 |
+------+-------------+---------+---------------+------------+------+------+

Querynya adalah sebagai berikut:

1. SELECT *,
2. IF(jenis_kelamin = "L", 1, NULL) AS L,
3. IF(jenis_kelamin = "P", 1, NULL) AS P
4. FROM mhs_status
5. LEFT JOIN mhs USING (nim)

Pada query diatas, ketika mengeksekusi klausa SELECT, MySQL akan


menambahkan dua kolom baru yaitu kolom L dan P. Selanjutnya
MySQL akan memeriksa satu per satu baris pada tabel hasil klausa
FROM ketika menjalankan klausa SELECT

Perhatikan ilustrasi berikut:

Killer Trik Query MySQL 87


Gambar 5.2 Ilustrasi Eksekusi Fungsi IF

Hal ini juga berlaku ketika kita menggunakan ekspresi CASE .. WHEN

Selanjutnya kita tambahkan fungsi COUNT untuk menghitung


jumlah baris pada kolom L dan P yang dikelompokkan berdasarkan
kolom status, kita ubah query diatas menjadi berikut:

1. SELECT status,
2. COUNT(IF(jenis_kelamin = "L", 1, NULL)) AS L,
3. COUNT(IF(jenis_kelamin = "P", 1, NULL)) AS P
4. FROM mhs
5. LEFT JOIN mhs_status USING (nim)
6. GROUP BY status

Ketika query diatas dijalankan, maka setelah selesai mengeksekusi


klausa SELECT, selanjutnya MySQL akan mengeksekusi fungsi
COUNT bersama dengan klausa GROUP BY Hasil eksekusi query
diatas adalah sebagai berikut:
+-------------+---+---+
| status | L | P |
+-------------+---+---+
| LULUS | 2 | 2 |
| TIDAK LULUS | 1 | 2 |
+-------------+---+---+

Untuk alur querynya, perhatikan ilustrasi berikut:

88 BAB 5 Menguasai Fungsi Agregasi


Gambar 5.3 Ilustrasi Eksekusi Fungsi COUNT + IF

Perhatikan bahwa pada step 2, kolom baru yang terbentuk bulan L


dan P melainkan sesuai dengan bentuk fungsi IF: IF(jenis_kelamin =
"L", 1, NULL) dan IF(jenis_kelamin = "P", 1, NULL) hal ini karena
kolom alias (L dan P) baru terbentuk setelah fungsi agregasi selesai
dieksekusi (tahap 3)

Killer Trik Query MySQL 89


Alur query diatas berlaku untuk semua fungsi agregasi seperti SUM()
dan AVG(), MAX(), dan MIN()

5.7. Studi Kasus: COUNT IF


Selanjutnya, setelah Anda memahami bagaimana alur query ketika
fungsi agregasi dan ekspresi logika di jalankan bersama, mari kita
bahas kasus sederhana berikut:

Misal kita memiliki tabel guru dengan data sebagai berikut:


+---------+-----------+----------+---------+
| id_guru | nama_guru | mengajar | id_kota |
+---------+-----------+----------+---------+
| G01 | Aldi | TK | 1 |
| G02 | Budi | SMP | 3 |
| G03 | Cyndi | SMA | 2 |
| G04 | Denny | SD | 2 |
| G05 | Erdi | SMP | 4 |
| G06 | Ferry | TK | 1 |
| G07 | Gari | SMA | 3 |
| G08 | Hari | SD | 1 |
| G09 | Indri | SD | 4 |
| G10 | Jeni | SMP | 4 |
+---------+-----------+----------+---------+

Selain itu kita juga memiliki tabel guru_kota dengan data sebagai
berikut:
+---------+-------------+
| kd_kota | nama_kota |
+---------+-------------+
| 1 | Jakarta |
| 2 | Yogayakarta |
| 3 | Semarang |
| 4 | Surakarta |
+---------+-------------+

Selanjutnya kita akan membuat report dengan menampilkan data


kota beserta jumlah guru untuk masing masing jenjang sekolah
dengan tabel output sebagai berikut:

90 BAB 5 Menguasai Fungsi Agregasi


+-------------+----+----+-----+-----+
| nama_kota | TK | SD | SMP | SMA |
+-------------+----+----+-----+-----+
| Jakarta | 2 | 3 | 3 | 3 |
| Yogayakarta | 0 | 2 | 2 | 2 |
| Semarang | 0 | 2 | 2 | 2 |
| Surakarta | 0 | 3 | 3 | 3 |
+-------------+----+----+-----+-----+

Bagaimana membuat querynya?

Dicoba dulu ya…

Gunakan selalu prinsip pada kunci menguasai SELECT

Sudah…?

Baiklah, mari kita bahas…

Pertama…
Seperti biasa… pertama kita buat tabel pada klausa FROM, namun
sebelumnya kita perlu mengidentifikasi tabel output

Dengan bentuk output seperti diatas, maka kita perlu tabel hasil
klausa FROM sebagai berikut:
+-------------+------+------+------+------+
| nama_kota | TK | SD | SMP | SMA |
+-------------+------+------+------+------+
| Jakarta | 1 | NULL | NULL | NULL |
| Semarang | NULL | NULL | 1 | NULL |
| Yogayakarta | NULL | NULL | NULL | 1 |
| Yogayakarta | NULL | 1 | NULL | NULL |
| Surakarta | NULL | NULL | 1 | NULL |
| Jakarta | 1 | NULL | NULL | NULL |
| Semarang | NULL | NULL | NULL | 1 |
| Jakarta | NULL | 1 | NULL | NULL |
| Surakarta | NULL | 1 | NULL | NULL |
| Surakarta | NULL | NULL | 1 | NULL |
+-------------+------+------+------+------+

Tabel tersebut diperoleh dari mana?

Killer Trik Query MySQL 91


Ingat kembali prinsip bahwa jika ingin menghitung, gunakan COUNT,
dan kali ini kita akan menghitung banyaknya guru yang mengajar
pada setiap jenjang pendidikan, untuk itu kita perlu menghitung
banyaknya baris pada tiap tiap jenjang. Perhatikan ilustrasi berikut:

Gambar 5.4 Tabel Awal Sebelum Tabel Output

Note: jika ada kasus terkait proses menghitung (COUNT), untuk


memudahkan penyusunan query, buat tabel awal seperti tabel
diatas

Ingat kembali bahwa COUNT tidak menghitung nilai NULL, sehingga


kita isi baris yang tidak ingin kita hitung dengan nilai NULL, sebaliknya
jika baris tersebut ingin kita masukkan dalam perhitungan, kita isi
dengan selain NULL, pada contoh diatas, kita gunakan angka 1

92 BAB 5 Menguasai Fungsi Agregasi


Selanjutnya berdasarkan tabel output, kita identifikasi tabel apa yang
akan kita gunakan pada klausa FROM:

1. Kita akan menampilkan data nama kota, data ini ada di tabel
guru_kota, sehingga kita akan menggunakan tabel guru_kota

2. Kita akan menghitung banyaknya guru berdasarkan jenjang,


selain itu kita juga akan membuat kolom TK, SD, SMP, dan SMA,
data ini terdapat pada tabel guru, maka kita gunakan tabel guru.

Dari analisa diatas, kita akan menggunakan tabel guru dan


guru_kota, sesuai ketentuan bahwa tabel hasil klausa FROM harus
berupa satu tabel, maka kita harus menggabungkan kedua tabel
tersebut menjadi satu

Selanjutnya, bagaimana bentuk penggabungannya, JOIN atau


UNION? Karena kita akan menggunakan kolom pada kedua tabel
(nama_kota dan jenjang (mengajar) dan keduanya saling
berhubungan (dengan kolom kd_kota) maka kita gunakan JOIN

Selanjutnya JOIN atau LEFT JOIN? Kali ini kita gunakan LEFT JOIN dan
kita tempatkan tabel guru_kota di sebelah kiri…

Kenapa?

Karena kita akan menampilkan semua data kota yang ada di tabel
guru_kota, lihat kembali bab Menguasai JOIN

Baiklah, mari kita tes hasil penggabungannya seperti apa, jalankan


query berikut:

1. SELECT *
2. FROM guru_kota
3. LEFT JOIN guru USING(kd_kota)

Killer Trik Query MySQL 93


Hasil yang kita peroleh:
+---------+-------------+---------+-----------+----------+
| kd_kota | nama_kota | id_guru | nama_guru | mengajar |
+---------+-------------+---------+-----------+----------+
| 1 | Jakarta | G01 | Aldi | TK |
| 3 | Semarang | G02 | Budi | SMP |
| 2 | Yogayakarta | G03 | Cyndi | SMA |
| 2 | Yogayakarta | G04 | Denny | SD |
| 4 | Surakarta | G05 | Erdi | SMP |
| 1 | Jakarta | G06 | Ferry | TK |
| 3 | Semarang | G07 | Gari | SMA |
| 1 | Jakarta | G08 | Hari | SD |
| 4 | Surakarta | G09 | Indri | SD |
| 4 | Surakarta | G10 | Jeni | SMP |
+---------+-------------+---------+-----------+----------+

Hasil sudah pas… kenapa? Bukankah kita perlu tabel dengan kolom
TK, SD, SMP, dan SMA? Seperti gambar V.1?

Betul…, coba perhatikan lagi adakah kolom tersebut (kolom TK, SD,
SMP, dan SMA) pada kedua tabel?

Tidak ada kan?

Nah karena kolom tersebut tidak ada pada tabel manapun, maka
kita perlu membuatnya, untuk membuatnya kita definisikan kolom
tersebut pada klausa SELECT, ingat, untuk menambah kolom,
gunakan klausa SELECT

Kedua…
Selanjutnya mari kita identifikasi bentuk tabel output untuk
menentukan kolom yang akan kita definisikan pada klausa SELECT

1. Nama Kota. Kolom ini sudah ada pada tabel hasil klausa FROM,
tinggal kita tulis saja

2. Kolom TK, SD, SMP, dan SMA, kolom ini tidak ada ada kedua
tabel, maka kita perlu membuatnya pada klausa SELECT

94 BAB 5 Menguasai Fungsi Agregasi


3. Kita perlu menghitung jumlah baris sesuai kolom baru yang kita
buat pada nomor 2

Lantas, bagaimana querynya?

Membuat kolom baru…


Ingat kembali pada bagian Kunci Memahami SELECT, ketika
mengeksekusi klausa SELECT, jika diperlukan, MySQL akan membuat
kolom baru, dan untuk memberi nilai kolom baru tersebut, maka
MySQL akan mengeksekusi setiap baris pada tabel hasil klausa
FROM

Jalankan query berikut:

1. SELECT nama_kota, IF(mengajar = "TK", 1, NULL) AS TK


2. , IF(mengajar = "SD", 1, NULL) AS SD
3. , IF(mengajar = "SMP", 1, NULL) AS SMP
4. , IF(mengajar = "SMA", 1, NULL) AS SMA
5. FROM guru_kota
6. LEFT JOIN guru USING(kd_kota)

Hasil yang kita peroleh:


+-------------+------+------+------+------+
| nama_kota | TK | SD | SMP | SMA |
+-------------+------+------+------+------+
| Jakarta | 1 | NULL | NULL | NULL |
| Semarang | NULL | NULL | 1 | NULL |
| Yogayakarta | NULL | NULL | NULL | 1 |
| Yogayakarta | NULL | 1 | NULL | NULL |
| Surakarta | NULL | NULL | 1 | NULL |
| Jakarta | 1 | NULL | NULL | NULL |
| Semarang | NULL | NULL | NULL | 1 |
| Jakarta | NULL | 1 | NULL | NULL |
| Surakarta | NULL | 1 | NULL | NULL |
| Surakarta | NULL | NULL | 1 | NULL |
+-------------+------+------+------+------+

Bagaimana prosesnya hingga terbentuk tabel seperti itu?

Killer Trik Query MySQL 95


Perhatikan ilustrasi berikut:

Gambar 5.5 Ilustrasi Eksekusi Fungsi IF

Penjelasan: seperti yang telah kita bahas sebelumnya, agar baris


tidak ikut dihitung, wajib kita isi dengan nilai NULL, sebaliknya, jika
baris tersebut ingin kita hitung kita isi dengan selain NULL, pada

96 BAB 5 Menguasai Fungsi Agregasi


contoh diatas kita gunakan angka 1, Anda bebas mengganti dengan
karakter lain asal bukan NULL

Selanjutnya kita hitung banyaknya baris untuk masing masing kolom


TK, SD, SMP, dan SMA. Untuk keperluan tersebut, kita gunakan
fungsi COUNT, ingat, fungsi agregasi termasuk COUNT akan
dieksekusi setelah klausa SELECT selesai dieksekusi, sehingga fungsi
ini akan diterapkan pada tabel:
+---------+-------------+---------+-----------+----------+------+------+------+------+
| kd_kota | nama_kota | id_guru | nama_guru | mengajar | TK | SD1 | SMP | SMA |
+---------+-------------+---------+-----------+----------+------+------+------+------+
| 1 | Jakarta | G01 | Aldi | TK | 1 | NULL | NULL | NULL |
| 3 | Semarang | G02 | Budi | SMP | NULL | NULL | 1 | NULL |
| 2 | Yogayakarta | G03 | Cyndi | SMA | NULL | NULL | NULL | 1 |
| 2 | Yogayakarta | G04 | Denny | SD | NULL | 1 | NULL | NULL |
| 4 | Surakarta | G05 | Erdi | SMP | NULL | NULL | 1 | NULL |
| 1 | Jakarta | G06 | Ferry | TK | 1 | NULL | NULL | NULL |
| 3 | Semarang | G07 | Gari | SMA | NULL | NULL | NULL | 1 |
| 1 | Jakarta | G08 | Hari | SD | NULL | 1 | NULL | NULL |
| 4 | Surakarta | G09 | Indri | SD | NULL | 1 | NULL | NULL |
| 4 | Surakarta | G10 | Jeni | SMP | NULL | NULL | 1 | NULL |
+---------+-------------+---------+-----------+----------+------+------+------+------+

Mari kita ubah querynya menjadi berikut:

1. SELECT nama_kota
2. , COUNT(IF(mengajar = "TK", 1, NULL)) AS TK
3. , COUNT(IF(mengajar = "SD", 1, 0)) AS SD1
4. , COUNT(IF(mengajar = "SMP", 1, 0)) AS SMP
5. , COUNT(IF(mengajar = "SMA", 1, 0)) AS SMA
6. FROM guru_kota
7. LEFT JOIN guru USING(kd_kota)
8. GROUP BY kd_kota

Jangan lupa untuk menambahkan klausa GROUP BY kd_kota agar data


dikelompokkan berdasarkan kode kota. Hasil yang kita peroleh
adalah:
+-------------+----+----+-----+-----+
| nama_kota | TK | SD | SMP | SMA |
+-------------+----+----+-----+-----+
| Jakarta | 2 | 3 | 3 | 3 |
| Yogayakarta | 0 | 2 | 2 | 2 |
| Semarang | 0 | 2 | 2 | 2 |
| Surakarta | 0 | 3 | 3 | 3 |
+-------------+----+----+-----+-----+

Killer Trik Query MySQL 97


Sudah pas dengan output kan? Yup… betul.

Untuk penjelasannya, perhatikan ilustrasi berikut:

Gambar 5.6 Ilustrasi Eksekusi Fungsi COUNT + IF

Sebagai tambahan, coba perhatikan kolom nama_kota, apakah Anda


menemukan sesuatu?

Yup, betul, data ditampilkan berurutan, lebih tepatnya berurutan


secara ascending berdasarkan kd_kota

Kenapa seperti itu? Ingat kembali tentang konsep GROUP BY, yaitu
sebelum menjalankan klausa GROUP BY, MySQL akan mengurutkan

98 BAB 5 Menguasai Fungsi Agregasi


data secara ascending berdasarkan kolom pada klausa GROUP BY,
yang dalam hal ini kolom kd_kota.

Pengayaan….
Selanjutnya untuk melatih skill Anda, kita kembangkan tabel output
diatas sehingga muncul kolom total di sebelah kanan seperti ini:
+-------------+----+-----+-----+-----+-------+
| nama_kota | TK | SD1 | SMP | SMA | total |
+-------------+----+-----+-----+-----+-------+
| Jakarta | 2 | 3 | 3 | 3 | 11 |
| Yogayakarta | 0 | 2 | 2 | 2 | 6 |
| Semarang | 0 | 2 | 2 | 2 | 6 |
| Surakarta | 0 | 3 | 3 | 3 | 9 |
+-------------+----+-----+-----+-----+-------+

Bagaimana querynya?

Langung saja…, querynya adalah sebagai berikut:

1. SELECT nama_kota
2. , COUNT(IF(mengajar = "TK", 1, NULL)) AS TK
3. , COUNT(IF(mengajar = "SD", 1, 0)) AS SD
4. , COUNT(IF(mengajar = "SMP", 1, 0)) AS SMP
5. , COUNT(IF(mengajar = "SMA", 1, 0)) AS SMA
6. , COUNT(IF(mengajar = "TK", 1, NULL))
7. + COUNT(IF(mengajar = "SD", 1, 0))
8. + COUNT(IF(mengajar = "SMP", 1, 0))
9. + COUNT(IF(mengajar = "SMA", 1, 0)) AS total
10. FROM guru_kota
11. LEFT JOIN guru USING(kd_kota)
12. GROUP BY kd_kota

Mungkin Anda penasaran kenapa pada kolom total kita tidak


gunakan kolom alias saja seperti ini:

1. SELECT nama_kota
2. , COUNT(IF(mengajar = "TK", 1, NULL)) AS TK
3. , COUNT(IF(mengajar = "SD", 1, 0)) AS SD
4. , COUNT(IF(mengajar = "SMP", 1, 0)) AS SMP
5. , COUNT(IF(mengajar = "SMA", 1, 0)) AS SMA

Killer Trik Query MySQL 99


6. , TK + SD + SMP + SMA AS total
7. FROM guru_kota
8. LEFT JOIN guru USING(kd_kota)
9. GROUP BY kd_kota

Jika kita jalankan query diatas, maka akan muncul pesan error bahwa
kolom TK tidak ditemukan…

SQL Error (1054): Unknown column 'TK' in 'field list'

Kenapa demikian?

Karena seperti yang telah kita bahas pada bab Konsekuensi Urutan
Eksekusi, bahwa kolom tambahan dari klausa SELECT hanya dapat
digunakan pada klausa GROUP BY dst…, sehingga nama kolom pada
tabel tersebut hanya bisa digunakan pada klausa GROUP BY dst…

Dengan demikian untuk menambahkan kolom total, kita perlu


menambahkan fungsi COUNT dan IF sebagai berikut:

1. SELECT nama_kota
2. , COUNT(IF(mengajar = "TK", 1, NULL)) AS TK
3. , COUNT(IF(mengajar = "SD", 1, 0)) AS SD
4. , COUNT(IF(mengajar = "SMP", 1, 0)) AS SMP
5. , COUNT(IF(mengajar = "SMA", 1, 0)) AS SMA
6. , COUNT(IF(mengajar = "TK", 1, NULL))
7. + COUNT(IF(mengajar = "SD", 1, 0))
8. + COUNT(IF(mengajar = "SMP", 1, 0))
9. + COUNT(IF(mengajar = "SMA", 1, 0)) AS total
10. FROM guru_kota
11. LEFT JOIN guru USING(kd_kota)
12. GROUP BY kd_kota
13. ORDER BY total DESC

100 BAB 5 Menguasai Fungsi Agregasi


Hasil:
+-------------+----+----+-----+-----+-------+
| nama_kota | TK | SD | SMP | SMA | total |
+-------------+----+----+-----+-----+-------+
| Jakarta | 2 | 3 | 3 | 3 | 11 |
| Surakarta | 0 | 3 | 3 | 3 | 9 |
| Semarang | 0 | 2 | 2 | 2 | 6 |
| Yogayakarta | 0 | 2 | 2 | 2 | 6 |
+-------------+----+----+-----+-----+-------+

Killer Trik Query MySQL 101


Halaman ini sengaja dikosongkan
Jagowebdev.com

102 BAB 5 Menguasai Fungsi Agregasi


BAB 6. Menguasai Fungsi Scalar
Fungsi scalar merupakan fungsi yang hanya melibatkan nilai tunggal,
berbeda dengan fungsi agregasi yang dalam operasinya melibatkan
banyak nilai. Fungsi ini umunya digunakan untuk mengubah nilai
kolom saat eksekusi dijalankan

6.1. Memahami Cara Kerja Fungsi Scalar


Seperti yang telah kita pelajari, ketika mengeksekusi klausa SELECT,
MySQL akan memeriksa baris tabel hasil klausa FROM satu persatu,
jika pada klausa SELECT tersebut terdapat fungsi scalar, maka nilai
pada baris akan diubah sesuai dengan output dari fungsi tersebut.

Contoh kita akan mengubah nama barang dari tabel barang menjadi
huruf besar semua, fungsi yang kita gunakan adalah UPPER. query
yang kita jalankan:

1. SELECT UPPER(nama_barang), stok


2. FROM barang;

Hasil:
+--------------------+------+
| UPPER(nama_barang) | stok |
+--------------------+------+
| MOUSE | 14 |
| FLASHDISK | 15 |
| MOUSEPAD | 17 |
| KEYBOARD | 12 |
| KABEL VGA | 7 |
+--------------------+------+

Killer Trik Query MySQL 103


Pada contoh diatas, setiap baris dari tabel barang akan di cek satu
persatu dan nilai pada kolom nama_barang akan diubah menjadi
huruf kapital. Perhatikan ilustrasi berikut:

Gambar 6.1. Ilustrasi Fungsi Scalar

Fungsi scalar jenisnya bermacam macam yang dapat dikelompokkan


menjadi empat jenis yaitu:

1. Fungsi terkait karakter. Fungsi ini digunakan untuk mengolah


string / teks, seperti SUBSTRING untuk mengambil bagian
tertentu dari string, UPPER, LOWER, dll

2. Fungsi terkait waktu, seperti DATE_FORMAT untuk mengubah


tampilan tanggal, YEAR untuk mengambil tahun dari tanggal,
dan MONTH untuk mengambil bulan, dll

3. Fungsi terkait perbedaan waktu, seperti: DATEDIFF untuk


menghitung perbedaan tanggal dalam hari, TIMEDIFF untuk
menghitung perbedaan waktu

4. Fungsi terkait numeric, seperti ROUND untuk membulatkan


pecahan, FLOOR untuk membulatkan pecahan kebawah, dll

104 BAB 6. Menguasai Fungsi Scalar


Kegunaan fungsi tersebut umumnya dapat dilihat dari namanya,
seperti UPPER, YEAR, dan MONTH sehingga tidak sulit menghafal
kegunaan dari fungsi tersebut.

Selanjutnya bagaimana mengetahui bahwa fungsi yang kita


digunakan adalah skalar? Untuk menghfalkanny mudah, kita tinggal
menghafal fungsi agregasi, selain itu merupakan fungsi skalar.

Pada bab ini kita tidak membahas semua fungsi scalar, kita hanya
akan membahas beberapa yang penting dan sering digunakan.

6.2. Fungsi String


SUBSTRING, SUBSTR, dan MID
Ketiga fungsi ini memiliki kegunaan yang sama peris, yaitu
mengambil bagian tertentu dari string, pemilihan ketiganya hanya
masalah kebiasaan saja, jika Anda terbiasa dengan PHP, maka akan
familiar dengan SUBSTR, sedangkan jika terbiasa dengan bahasa
pemrograman keluarga Microsoft akan familiar dengan MID

Format penulisan ketiga fungsi tersebut adalah sebagai berikut:

SUBSTRING(teks, posisi_awal, jumlah_karakter);

Contoh:
mysql> SELECT SUBSTRING('abcde', 3, 2);
+--------------------------+
| SUBSTRING('abcde', 3, 2) |
+--------------------------+
| cd |
+--------------------------+
1 row in set (0.00 sec)

mysql> SELECT SUBSTRING('abcde', 3);


+-----------------------+

Killer Trik Query MySQL 105


| SUBSTRING('abcde', 3) |
+-----------------------+
| cde |
+-----------------------+
1 row in set (0.00 sec)

mysql> SELECT SUBSTRING('abcde', -3, 2);


+---------------------------+
| SUBSTRING('abcde', -3, 2) |
+---------------------------+
| cd |
+---------------------------+
1 row in set (0.00 sec)

mysql> SELECT SUBSTRING('abcde', -3);


+------------------------+
| SUBSTRING('abcde', -3) |
+------------------------+
| cde |
+------------------------+
1 row in set (0.00 sec)

Contoh penerapan pada tabel:

1. SELECT SUBSTRING(nama_barang, 1, 4), stok


2. FROM barang;

Hasil:
+------------------------------+------+
| SUBSTRING(nama_barang, 1, 4) | stok |
+------------------------------+------+
| Mous | 14 |
| Flas | 15 |
| Mous | 17 |
| Keyb | 12 |
| Kabe | 7 |
+------------------------------+------+

CONCAT dan CONCAT_WS


Fungsi concat digunakan untuk menggabungkan ekspresi menjadi
satu, ekspresi ini dapat berupa nilai skalar, string, nilai kolom, dll.
Adapun format penulisannya adalah sebagai berikut

106 BAB 6. Menguasai Fungsi Scalar


CONCAT(ekspresi1, ekspresi2, ekspresi3, ...)

Contoh:

SELECT CONCAT("Stok: ", 14);

Hasil:
+----------------------+
| CONCAT('Stok: ', 14) |
+----------------------+
| Stok: 14 |
+----------------------+

Contoh penerapan pada tabel:

1. SELECT CONCAT(nama_barang, ", Stok: ", 14)


2. FROM barang

Hasil:
+---------------------------------------+
| CONCAT(nama_barang, ", Stok: ", stok) |
+---------------------------------------+
| Mouse, Stok: 14 |
| Flashdisk, Stok: 15 |
| Mousepad, Stok: 17 |
| Keyboard, Stok: 12 |
| Kabel VGA, Stok: 7 |
+---------------------------------------+

Selain fungsi concat, MySQL juga menyediakan fungsi CONCAT_WS


(CONCAT With Separator). Fungsi ini sama seperti CONCAT, bedanya
argumen pertama dari fungsi ini akan digunakan sebagai separator,
misal:

1. SELECT CONCAT_WS(", ", nama_barang, stok, harga)


2. FROM barang

Killer Trik Query MySQL 107


Hasil:
+-------------------------------------------+
| CONCAT_WS(", ", nama_barang, stok, harga) |
+-------------------------------------------+
| Mouse, 14, 76000 |
| Flashdisk, 15, 55000 |
| Mousepad, 17, 35000 |
| Keyboard, 12, 80000 |
| Kabel VGA, 7, 45000 |
+-------------------------------------------+

6.3. Fungsi Date Time


MySQL menyediakan berbagai fungsi yang dapat digunakan untuk
memanipulasi tanggal dan waktu, diantaranya yang penting untuk
diketahui adalah YEAR, MONTH, DAY, DATE_FORMAT, dan
STR_TO_DATE

Standar Format Tanggal


Sebelum membahas fungsi terkait tanggal dan waktu, kita perlu
memahami format tanggal standar yang digunakan oleh database.

Standar format tanggal untuk database termasuk yang digunakan


MySQL adalah YYYY-MM-DD HH:MM:SS, misal: 2017-06-01
08:00:00. Untuk tanggal, separator yang digunakan bisa bermacam
macam, yang penting urutnnya YYYY diikuti MM kemudian DD, misal:
2017/06/01, 2017,06,01, bisa juga tanpa separator misal: 20170601.

Agar hasil dari fungsi tanggal dan waktu benar, maka format
tanggal (termasuk tanggal pada kolom) harus standar sesuai
yang telah kita bahas diatas.

YEAR, MONTH, DAY

108 BAB 6. Menguasai Fungsi Scalar


Sesuai terjemahannya, fungsi ini digunakan untuk mengambil
Tahun, bulan, dan tanggal dari data tanggal.

Digit bulan dan tanggal yang dihasilkan oleh fungsi ini tidak
diawali dengan 0, seperti 1 bukan 01

Sebagai contoh misal kita memiliki tabel penjulan sebagai berikut:


+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 1 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Selanjutnya kita tampilkan data tersebut dengan memisahkan


antara tahun, bulan, dan tanggal, jalankan query berikut:

1. SELECT YEAR(tgl_trx)
2. , MONTH(tgl_trx)
3. , DAY(tgl_trx)
4. , total_trx
5. FROM penjualan

Hasil yang kita peroleh:


+---------------+----------------+--------------+-----------+
| YEAR(tgl_trx) | MONTH(tgl_trx) | DAY(tgl_trx) | total_trx |
+---------------+----------------+--------------+-----------+
| 2017 | 3 | 2 | 192000 |
| 2017 | 3 | 10 | 186000 |
| 2017 | 4 | 10 | 259000 |
| 2017 | 4 | 5 | 110000 |
| 2016 | 11 | 10 | 256000 |
+---------------+----------------+--------------+-----------+

Killer Trik Query MySQL 109


DATE_FORMAT
Fungsi DATE_FORMAT digunakan untuk mengubah tanggal format
standar database (YYYY-MM-DD) menjadi bentuk lain. Adapun
format penulisannya adalah:

DATE_FORMAT(tanggal, format_tanggal)

Argumen format_tanggal berupa string yang mengandung


karakter tertentu yang mewakili bentuk tanggal tertentu .Berikut
ini format karakter yang penting untuk diketahui:

Format Deskripsi

Hari

%W Nama hari dalam satu minggu (Sunday s.d Saturday)

%w Hari dalam satu minggu (0 = Minggu s.d. 6 = Sabtu)

%d Hari dalam satu bulan dalam dua digit (00 s.d 31)

%e Hari dalam satu bulan dalam satu-dua digit (0 s.d 31)

Bulan

%M Nama bulan penuh (January s.d December)

%c Bulan dalam satu-dua digit angka (0 s.d 12)

%m Bulan dalam dua digit angka (00 s.d 12)

Tahun

%Y Tahun dalam empat digit angka

%y Tahun dalam dua digit angka

Jam

%H Jam dalam 24 jam dalam dua digit (00 .d 23)

110 BAB 6. Menguasai Fungsi Scalar


%k Jam dalam 24 jam dalam satu-dua digit (0 s.d 23)

Menit

%i Menit dalam dua dua digit (00 s.d 59)

Detik

%S Detik dalam dua digit (00 s.d 59)

%s Detik dalam satu digit (1s.d 59)

Catatan: Tanda persen (%) menandakan bahwa karakter yang


mengikutinya adalah karakter khusus untuk format tanggal,
sehingga kita wajib menyertakannya.

Contoh:

1. SELECT tgl_trx
2. , DATE_FORMAT(tgl_trx, "%d-%m-%Y") AS tgl_trx_id
3. , total_trx
4. FROM penjualan

Hasil:
+------------+------------+-----------+
| tgl_trx | tgl_trx_id | total_trx |
+------------+------------+-----------+
| 2017-03-02 | 02-03-2017 | 192000 |
| 2017-03-10 | 10-03-2017 | 186000 |
| 2017-04-10 | 10-04-2017 | 259000 |
| 2017-04-05 | 05-04-2017 | 110000 |
| 2016-11-10 | 10-11-2016 | 256000 |
+------------+------------+-----------+

Contoh lain, kita gunakan nama hari dan nama bulan

1. SET lc_time_names = "id_ID";


2. SELECT tgl_trx
3. , DATE_FORMAT(tgl_trx, "%W, %e %M %Y") AS tgl_trx_id
4. , total_trx
5. FROM penjualan

Killer Trik Query MySQL 111


Hasil:
+------------+-------------------------+-----------+
| tgl_trx | tgl_trx_id | total_trx |
+------------+-------------------------+-----------+
| 2017-03-02 | Kamis, 2 Maret 2017 | 192000 |
| 2017-03-10 | Jumat, 10 Maret 2017 | 186000 |
| 2017-04-10 | Senin, 10 April 2017 | 259000 |
| 2017-04-05 | Rabu, 5 April 2017 | 110000 |
| 2016-11-10 | Kamis, 10 November 2016 | 256000 |
+------------+-------------------------+-----------+

Perhatikan bahwa pada contoh diatas, kita ubah variabel


lc_time_names menjadi Indonesia (id_ID) jika tidak, maka nama hari
dan bulan akan ditampilkan dalam bahasa Inggris.

STR_TO_DATE
Fungsi STR_TO_DATE digunakan untuk mengubah format tanggal
Non-Standard SQL menjadi format tanggal standar SQL. Fungsi ini
merupakan fungsi yang penting dan sering digunakan untuk itu
harus Anda kuasai.

Kenapa perlu mengubah format tanggal menjadi format


standar SQL? Sering kita temui format tanggal yang disimpan
dalam database banyak yang tidak standar, ada yang
dd/mm/yyyy, dd-mm-yyyy, dmyyyy dll.

Nah, untuk dapat mengolah data tersebut menggunakan


fungsi terkait date seperti DATE_FORMAT(), YEAR(), MONTH(), dan
DAY(), maka data tersebut harus diubah ke dalam bentuk
standar SQL

Format penulisan fungsi STR_TO_DATE adalah:

STR_TO_DATE(string_date, format_tanggal)

112 BAB 6. Menguasai Fungsi Scalar


Argumen format_tanggal berupa string yang mengandung karakter
tertentu yang mewakili bentuk tanggal tertentu. Karakter tertentu ini
sama persis dengan yang digunakan pada fungsi DATE_FORMAT

Argumen format_tanggal ini nantinya akan digunakan MySQL untuk


membandingkan pola tanggal yang ada pada argumen pertama.

Contoh:

1. SELECT STR_TO_DATE ('25/05/2017', '%d/%m/%Y') AS tanggal

Hasil:
+------------+
| tanggal |
+------------+
| 2017-05-25 |
+------------+

Pada contoh diatas, MySQL akan men-scan tanggal satu per satu
mulai dari kiri ke kanan kemudian mencocokkan dengan pola pada
argumen 2. Format yang kita gunakan pada argumen ke 2 adalah
%d/%m/%Y:

 %d, karena tanggal pada argumen pertama berbentuk dua digit


yaitu 05

 %m karena bulan juga berbentuk dua digit, yaitu 03

 %Y karena tahun berbentuk 4 digit, yaitu 2017

 Kita gunakan separator slash ( / ) karena antara tanggal, bulan,


dan tahun dipisahkan dengan separator tersebut

Jika diilustrasikan dengan gambar, akan tampak seperti gambar


berikut:

Killer Trik Query MySQL 113


Gambar 6.2. Ilustrasi Fungsi STR_TO_DATE()

Jika kita salah dalam mengidentifikasi format tanggal, maka hasil


yang diperoleh tidak sesuai harapan, misal kita keliru menulis %Y
menjadi %y:

SELECT STR_TO_DATE ('05/03/2017', '%d/%m/%y') AS tanggal

Maka hasil yang kita peroleh:


+------------+
| tanggal |
+------------+
| 2020-03-05 |
+------------+

Dari contoh diatas terlihat bahwa tahun yang seharusnya 2017


menjadi 2020, hal ini karena format %y membaca data tahun
sebanyak dua digit, sehingga, tahun 2017 akan dibaca tahun 20
kemudian dikonversi ke format standar SQL menjadi 2020.

Jika format pada argumen ke 2 tidak sesuai dengan format pada


argumen pertama (meskipun hanya satu format), maka akan
menghasilkan NULL, misal:

SELECT STR_TO_DATE ('05-03-2017', '%d-%M-%y') AS tanggal

Hasil:

114 BAB 6. Menguasai Fungsi Scalar


+---------+
| tanggal |
+---------+
| NULL |
+---------+

Query diatas menghasilkan NULL karena:

 Pertama, MySQL akan mencari format %d dan ketemu di dua


digit pertama (05)
 Selanjutnya mencari tanda dash (-) dan ketemu yaitu setelah 05
 MySQL melanjutkan dengan mencari format %M yang artinya
nama bulan (Januari s.d December), namun tidak ketemu,
karena karakter selanjutnya adalah 03, sehingga proses
pencarian berhenti dan menghasilkan NULL

6.4. Fungsi Beda Waktu


MySQL menyediakan fungsi untuk menghitung beda waktu, fungsi
yang penting untuk dipahami adalah fungsi DATEDIFF dan TIMEDIFF

DATEDIFF
Fungsi DATEDIFF() digunakan untuk mencari selisih tanggal dalam
hari. Fungsi ini memiliki dua buah argumen yang wajib diisi, yaitu: (1)
tanggal akhir dan (2) tanggal awal. Format penulisannya adalah:

DATEDIFF (tanggal_akhir, tanggal_awal)

Contoh penggunaan:

SELECT DATEDIFF("2017-05-30","2016-03-25") AS selisih

Hasil:

Killer Trik Query MySQL 115


+---------+
| selisih |
+---------+
| 431 |
+---------+

Contoh penggunaan pada tabel:

Misal kita memiliki tabel buku_pinjam yang berisi data peminjaman


buku dengan data sebagai berikut:
+-----------+---------+-------------+------------+-------------+
| id_pinjam | id_buku | id_peminjam | tgl_pinjam | tgl_kembali |
+-----------+---------+-------------+------------+-------------+
| 1 | 1 | 001 | 2017-05-13 | 0000-00-00 |
| 2 | 2 | 002 | 2017-05-10 | 0000-00-00 |
| 3 | 3 | 001 | 2017-05-12 | 2017-05-15 |
| 4 | 4 | 003 | 2017-05-14 | 0000-00-00 |
| 5 | 3 | 004 | 2017-05-12 | 2017-05-14 |
+-----------+---------+-------------+------------+-------------+

Selanjutnya kita hitung lamanya peminjaman buku, jalankan query


berikut:

1. SELECT id_buku
2. , DATEDIFF(tgl_kembali, tgl_pinjam) AS lama_pinjam
3 FROM buku_pinjam;

Hasil:
+---------+-------------+
| id_buku | lama_pinjam |
+---------+-------------+
| 1 | NULL |
| 2 | NULL |
| 3 | 3 |
| 4 | NULL |
| 3 | 2 |
+---------+-------------+

116 BAB 6. Menguasai Fungsi Scalar


TIMEDIFF
Fungsi TIMEDIFF digunakan untuk menghitung beda waktu. Format
penulisannya adalah sebagai berikut:

SELECT TIMEDIFF(waktu_akhir, waktu_awal)

Waktu_akhir dan waktu_awal harus memiliki format sama,


misal jika waktu_akhir date time, seperti 2017-06-01 07:26:00,
maka waktu_awal juga harus date time, jika hanya tima, misal
07:30:00, maka output yang diperoleh tidak sesuai yang
diharapkan

Contoh penggunaan:

1. SELECT TIMEDIFF("07:05:30", "07:05:01");

Hasil:
+----------------------------------+
| TIMEDIFF("07:05:30", "07:05:01") |
+----------------------------------+
| 00:00:29 |
+----------------------------------+

Contoh penggunaan pada tabel. Misal kita memiliki tabel absen


sebagai berikut:
+----------+------------+---------------------+
| id_absen | id_pegawai | waktu_absen |
+----------+------------+---------------------+
| 1 | 1 | 2017-05-14 07:22:59 |
| 2 | 1 | 2017-05-14 07:27:12 |
| 3 | 2 | 2017-05-14 07:29:25 |
| 4 | 3 | 2017-05-14 07:31:01 |
| 5 | 4 | 2017-05-14 07:59:07 |
+----------+------------+---------------------+

Killer Trik Query MySQL 117


Selanjutnya kita hitung waktu keterlambatan dengan batas waktu
absen 07:30, jalankan query berikut:

1. SELECT id_pegawai, TIMEDIFF(TIME(waktu_absen), "07:30:00")


2. FROM absensi

Hasil:
+------------+-----------------------------------------+
| id_pegawai | TIMEDIFF(TIME(waktu_absen), "07:30:00") |
+------------+-----------------------------------------+
| 1 | -00:07:01 |
| 1 | -00:02:48 |
| 2 | -00:00:35 |
| 3 | 00:01:01 |
| 4 | 00:29:07 |
| 5 | 00:30:00 |
+------------+-----------------------------------------+

Pada contoh diatas, kita gunakan fungsi TIME untuk mengambil


waktu dari date time yang ada pada kolom waktu_absen

6.5. Fungsi Numeric


MySQL menyediakan banyak fungsi untuk memanipulasi angka
(numeric), tiga fungsi terpenting yang harus dipahami adalah
ROUND, CEIL, dan FLOOR

ROUND
Fungsi ROUND digunakan untuk membulatkan desimal (angka di
belakang koma). Jika angka di belakang koma bernilai 5 atau lebih,
maka akan dibulatkan ketas, jika kurang dari 5 maka akan dibulatkan
ke bawah. Adapun format penulisannya adalah:

ROUND(bilangan, jumlah_desimal)

118 BAB 6. Menguasai Fungsi Scalar


Argumen jumlah desimal menunjukkan banyaknya angka di
belakang koma. Argumen ini bersifat opsional, jika tidak diisi, maka
akan bernilai 0.

Contoh:

SELECT ROUND(2.49), ROUND(2.50), ROUND(2.7159, 2), ROUND(-2.50)

Hasil:
+-------------+-------------+------------------+--------------+
| ROUND(2.49) | ROUND(2.50) | ROUND(2.7159, 2) | ROUND(-2.50) |
+-------------+-------------+------------------+--------------+
| 2 | 3 | 2.72 | -3 |
+-------------+-------------+------------------+--------------+

Contoh penerapan pada tabel: misal kita memiliki tabel


penjualan_detail sebagai berikut:
+-----------+-------------+-------+------------+
| kd_barang | nama_barang | harga | harga_jual |
+-----------+-------------+-------+------------+
| 1 | Mouse | 76000 | 74000 |
| 2 | Flashdisk | 55000 | 55000 |
| 3 | Mousepad | 35000 | 30000 |
| 4 | Keyboard | 80000 | 75000 |
| 5 | Kabel VGA | 45000 | 45000 |
+-----------+-------------+-------+------------+

Selanjutnya kita akan menghitung nilai diskon dalam rupiah. Query


yang kita jalankan:

1. SELECT kd_barang, harga, harga_jual


2. , CONCAT( ROUND( (harga - harga_jual) / harga * 100 )
3. , "%"
4. ) AS diskon
5. FROM barang;

Hasil:
+-----------+-------+------------+--------+
| kd_barang | harga | harga_jual | diskon |

Killer Trik Query MySQL 119


+-----------+-------+------------+--------+
| 1 | 76000 | 74000 | 3% |
| 2 | 55000 | 55000 | 0% |
| 3 | 35000 | 30000 | 14% |
| 4 | 80000 | 75000 | 6% |
| 5 | 45000 | 45000 | 0% |
+-----------+-------+------------+--------+

FLOOR dan CEIL


Fungsi FLOOR() digunakan untuk melakukan pembulatan desimal ke
bawah. Format penulisannya adalah:

FLOOR(bilangan)

Contoh:

SELECT FLOOR(2.9), FLOOR(-2.10), FLOOR(-2.90)

Hasil:
+------------+--------------+--------------+
| FLOOR(2.9) | FLOOR(-2.10) | FLOOR(-2.90) |
+------------+--------------+--------------+
| 2 | -3 | -3 |
+------------+--------------+--------------+

Contoh penerapan fungsi ini adalah ketika kita mencari rata-rata


nilai siswa, misal kita memiliki tabel nilai sebagai berikut:
+----------+---------+-------+
| id_siswa | nama | nilai |
+----------+---------+-------+
| 1 | Alfa | 83 |
| 2 | Beta | 77 |
| 3 | Charlie | 64 |
| 4 | Delta | 73 |
+----------+---------+-------+

Query yang kita jalankan:

1. SELECT AVG(nilai) AS rata_rata,

120 BAB 6. Menguasai Fungsi Scalar


2. FLOOR( AVG(nilai) ) AS floor_rata_rata
3. FROM nilai

Hasil:
+-----------+-----------------+
| rata_rata | floor_rata_rata |
+-----------+-----------------+
| 74.25 | 74 |
+-----------+-----------------+

Pada contoh diatas, pertama-tama MySQL akan menjalankan fungsi


AVG() untuk menghitung rata-rata nilai, selanjutnya MySQL
menjalankan fungsi FLOOR() untuk melakukan pembulatan ke bawah.

Kebalikan dari fungsi FLOOR(), fungsi CEIL() digunakan untuk


melakukan pembulatan ke atas, misal kita ingin mencari nilai-rata-
rata dan membulatkannya ke atas, kita jalankan query berikut:

1. SELECT AVG(nilai) AS rata_rata,


2. FLOOR( AVG(nilai) ) AS floor_rata_rata,
3. CEIL( AVG(nilai) ) AS ceil_rata_rata
4. FROM nilai

Hasil yang kita peroleh:


+-----------+-----------------+----------------+
| rata_rata | floor_rata_rata | ceil_rata_rata |
+-----------+-----------------+----------------+
| 74.25 | 74 | 75 |
+-----------+-----------------+----------------+

Killer Trik Query MySQL 121


Halaman ini sengaja dikosongkan
Jagowebdev.com

122 BAB 6. Menguasai Fungsi Scalar


BAB 7 Pendalaman Materi: Fungsi
Agregasi, Skalar, Logika
Pada bab bab sebelumnya, kita telah membahas fungsi agregasi,
fungsi scalar, dan ekspresi logika, pada bab ini, ketiganya akan kita
bahas lebih dalam, karena topik tersebut sering digunakan dalam
praktik.

7.1. Fungsi Scalar Pada WHERE


Fungsi scalar dapat digunakan di berbagai tempat, selain pada
klausa SELECT, fungsi ini sering digunakan pada klausa WHERE, misal
kita akan menampilkan data penjualan bulan maret tahun 2017
pada tabel penjualan berikut ini:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 1 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Query yang kita jalankan:

1. SELECT id_pelanggan, tgl_trx, total_trx


2. FROM penjualan
3. WHERE MONTH(tgl_trx) = 4 AND YEAR(tgl_trx) = 2017

Hasil:

Killer Trik Query MySQL 123


+--------------+------------+-----------+
| id_pelanggan | tgl_trx | total_trx |
+--------------+------------+-----------+
| 1 | 2017-04-10 | 259000 |
| 2 | 2017-04-05 | 110000 |
+--------------+------------+-----------+

Penjelasan: Pada bagian awal telah di bahas bahwa ketika


menjalankan klausa WHERE, MySQL akan memerika satu per satu
baris tabel yang dihasilkan klausa FROM, jika baris tersebut
memenuhi kriteria klausa WHERE, maka ambil baris tersebut.
Perhatikan ilustrasi berikut:

Gambar 7.1 Ilustrasi Urutan Eksekusi Query

124 BAB 7 Pendalaman Materi: Fungsi Agregasi, Skalar, Logika


7.2. Fungsi Scalar Pada GROUP BY
Selain pada WHERE, fungsi Scalar juga sering digunakan pada klausa
GROUP BY, misal kita ambil data penjualan tahun 2017 dan kita
kelompokkan data tersebut berdasarkan bulan transaksi. Query
yang kita jalankan adalah:

1. SELECT MONTH(tgl_trx) AS bulan, SUM(total_trx) AS total


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. GROUP BY bulan

Hasil:
+-------+--------+
| bulan | total |
+-------+--------+
| 3 | 378000 |
| 4 | 369000 |
+-------+--------+

Bagaimana alur dari query diatas?

Seperti konsep eksekusi query:

Pertama, MySQL akan mengeksekusi klausa FROM dan WHERE,


sehingga akan terbentuk temporary tabel sebagai berikut:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 1 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
+--------+--------------+------------+-----------+

Kedua, MySQL akan mengeksekusi klausa SELECT, dan


menambahkan kolom baru (jika ada), namun untuk fungsi agregasi,
MySQL akan menjalankannya bersamaan dengan klausa GROUP BY,

Killer Trik Query MySQL 125


dengan demikian, MySQL akan membentuk temporary tabel lagi
seperti ini:
+--------+--------------+------------+-----------+-------+
| id_trx | id_pelanggan | tgl_trx | total_trx | bulan |
+--------+--------------+------------+-----------+-------+
| 1 | 1 | 2017-03-02 | 192000 | 3 |
| 2 | 1 | 2017-03-10 | 186000 | 3 |
| 3 | 1 | 2017-04-10 | 259000 | 4 |
| 4 | 2 | 2017-04-05 | 110000 | 4 |
+--------+--------------+------------+-----------+-------+

Ketiga, MySQL akan menjalankan klausa GROUP BY bersama dengan


fungsi agregasi SUM, kemudian mengambil kolom sesuai dengan
statemen SELECT sehingga diperoleh hasil:
+-------+--------+
| bulan | total |
+-------+--------+
| 3 | 378000 |
| 4 | 369000 |
+-------+--------+

Perhatikan ilustrasi berikut:

126 BAB 7 Pendalaman Materi: Fungsi Agregasi, Skalar, Logika


Gambar 7.2 Ilustrasi Urutan Eksekusi Query

Jika tanpa kolom alias, maka query menjadi:

1. SELECT MONTH(tgl_trx), SUM(total_trx) AS total


2. FROM penjualan
3. WHERE YEAR(tgl_trx) = 2017
4. GROUP BY MONTH(tgl_trx)

7.3. Studi Kasus


Dalam praktik, kita akan sering menemukan kondisi dimana kita
harus menggunakan beberapa fungsi sekaligus, baik fungsi logika IF,
Fungsi Agregasi, maupun Fungsi Scalar.

Killer Trik Query MySQL 127


Kondisi di lapangan bisa bermacam macam, namun yang terpenting
Anda paham konsepnya, bagaimana query dieksekusi dan
bagaimana tabel output dihasilkan.

Mari kita lanjutkan….

Contoh kasus ini akan menguji pemahaman Anda mengenai fungsi


logika IF, Fungsi Agregasi, dan Fungsi Scalar

Tabel yang akan kita gunakan adalah tabel pelanggan dan tabel
penjualan sebagai berikut:
pelanggan
+--------------+---------+-----------+---------+
| id_pelanggan | nama | alamat | id_staf |
+--------------+---------+-----------+---------+
| 1 | Alfa | Jakarta | 1 |
| 2 | Beta | Semarang | 1 |
| 3 | Charlie | Surabaya | 2 |
| 4 | Delta | Surakarta | 3 |
+--------------+---------+-----------+---------+
penjualan
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 1 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Selanjutnya kita akan membuat resume total semua penjualan per


pelanggan per bulan untuk tahun 2017 dengan format sebagai
berikut:
+--------------+------+---------+----------+--------+--------+
| id_pelanggan | nama | Januari | Februari | Maret | April |
+--------------+------+---------+----------+--------+--------+
| 1 | Alfa | 0 | 0 | 378000 | 259000 |
| 2 | Beta | 0 | 0 | 0 | 110000 |
+--------------+------+---------+----------+--------+--------+

128 BAB 7 Pendalaman Materi: Fungsi Agregasi, Skalar, Logika


Silakan Anda coba, pasti bisa… !!!

Tips: selalu gunakan konsep alur eksekusi query dan fungsi yang
telah kita pelajari…

Sudah… ?

Oke, mari kita bahas….

Pertama…

Pertama selalu kita buat klausa FROM nya terlebih dahulu dan jika
perlu klausa WHERE

Dengan menganalisa tabel output, maka dapat disimpulkan bahwa


kita perlu menggunakan kedua tabel (tabel pelanggan dan tabel
penjualan) selain itu, kita gunakan klausa WHERE untuk membatasi
data yang diambil hanya data tahun 2017.

Karena kita akan menggabungkan kedua tabel maka kita perlu


menggunakan JOIN, dan pilihannya ada dua JOIN atau LEFT JOIN.
Nah karena fokus kita adalah per penjualan maka kita akan
menampilkan semua data penjualan, untuk itu kita gunakan LEFT
JOIN dan kita letakkan tabel penjualan disebelah kiri

Kita coba hasil penggabungannya dengan klausa SELECT * sebagai


berikut:

1. SELECT *
2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan)
4. WHERE YEAR(tgl_trx) = 2017

Hasilnya adalah:

Killer Trik Query MySQL 129


+--------------+--------+------------+-----------+------+----------+---------+
| id_pelanggan | id_trx | tgl_trx | total_trx | nama | alamat | id_staf |
+--------------+--------+------------+-----------+------+----------+---------+
| 1 | 1 | 2017-03-02 | 192000 | Alfa | Jakarta | 1 |
| 1 | 2 | 2017-03-10 | 186000 | Alfa | Jakarta | 1 |
| 2 | 4 | 2017-04-05 | 110000 | Beta | Semarang | 1 |
| 0 | 3 | 2017-04-10 | 259000 | NULL | NULL | NULL |
+--------------+--------+------------+-----------+------+----------+---------+

Sekarang kita cek, apakah semua transaksi sudah muncul?

dan tahun transaksinya sudah 2017?

Yub, benar, berarti penggabungan sudah benar…

Kedua…

Selanjutnya kita susun klausa SELECT

Sebelumnya mari kita analisa kolom pada tabel output:

1. Kolom id_pelanggan dan nama pelanggan, keduanya sudah ada


pada tabel hasil klausa FROM, tinggal kita ambil

2. Kolom Januari, Februari, Maret, dan April belum ada, maka kita
buat kolom tersebut. Ingat bab Kunci Menguasai SELECT, untuk
membuat kolom baru, kita definisikan kolom tersebut pada
bagian SELECT

Bagaimana kita mendapatkan penjualan bulan Januari, Februari,


dst… ?

Jika kita amati tabel hasil klausa FROM, untuk mendapatkan data
bulan, kita harus mengambil bulan pada kolom tgl_trx, maka query
yang kita perlukan adalah sebagai berikut:

1. SELECT id_pelanggan
2. , nama
3. , IF(MONTH(tgl_trx) = 1, total_trx, 0) AS Januari
4. , IF(MONTH(tgl_trx) = 2, total_trx, 0) AS Februari

130 BAB 7 Pendalaman Materi: Fungsi Agregasi, Skalar, Logika


5. , IF(MONTH(tgl_trx) = 3, total_trx, 0) AS Maret
6. , IF(MONTH(tgl_trx) = 4, total_trx, 0) AS April
7. FROM penjualan
8. LEFT JOIN pelanggan USING (id_pelanggan)
9. WHERE YEAR(tgl_trx) = 2017

Hasilnya adalah:
+--------------+------+---------+----------+--------+--------+
| id_pelanggan | nama | Januari | Februari | Maret | April |
+--------------+------+---------+----------+--------+--------+
| 1 | Alfa | 0 | 0 | 192000 | 0 |
| 1 | Alfa | 0 | 0 | 186000 | 0 |
| 2 | Beta | 0 | 0 | 0 | 110000 |
| 0 | NULL | 0 | 0 | 0 | 259000 |
+--------------+------+---------+----------+--------+--------+

Bagaimana itu bisa terjadi?

Kita flash back lagi tentang fungsi IF, jika argumen paling kiri bernilai
benar, maka eksekusi argumen bagian tengah, jika salah eksekusi
argumen bagian paling kanan.

Berikut ini ilustrasinya ( kolom Januari dan Februari dihilangkan


untuk menyederhanakan tampilan ).

Gambar 7.3 Ilustrasi Eksekusi Query

Killer Trik Query MySQL 131


Ketiga…
Selanjutnya kita jumlahkan kolom Januari, Februari, dst… untuk
mendapatkan nilai total per bulan per pelanggan. Karena data
dikelompokkan per pelanggan, jangan lupa untuk menambahkan
klausa GROUP BY

Bentuk query jadinya adalah sebagai berikut:

1. SELECT id_pelanggan
2. , nama
3. , SUM(IF(MONTH(tgl_trx) = 1, total_trx, 0)) AS Januari
4. , SUM(IF(MONTH(tgl_trx) = 2, total_trx, 0)) AS Februari
5. , SUM(IF(MONTH(tgl_trx) = 3, total_trx, 0)) AS Maret
6. , SUM(IF(MONTH(tgl_trx) = 4, total_trx, 0)) AS April
7. FROM penjualan
8. LEFT JOIN pelanggan USING (id_pelanggan)
9. WHERE YEAR(tgl_trx) = 2017
10. GROUP BY id_pelanggan

Hasil:
+--------------+------+---------+----------+--------+--------+
| id_pelanggan | nama | Januari | Februari | Maret | April |
+--------------+------+---------+----------+--------+--------+
| 0 | NULL | 0 | 0 | 0 | 259000 |
| 1 | Alfa | 0 | 0 | 378000 | 0 |
| 2 | Beta | 0 | 0 | 0 | 110000 |
+--------------+------+---------+----------+--------+--------+

Penjelasan: lihat pembahasan pada sub bab sebelumnya Fungsi


Scalar Pada GROUP BY

132 BAB 7 Pendalaman Materi: Fungsi Agregasi, Skalar, Logika


BAB 8 Mempertajam Agregasi dan
JOIN
Untuk mempertajam pemahaman Anda tentang bagaimana Fungsi
Agregasi dan klausa JOIN bekerja serta mengasah kemampuan Anda
memecahkan permasalahan terkait penyusunan query SQL, mari
kita bahas beberapa contoh kasus.

8.1. Studi Kasus #1


Sebagai latihan, kita coba dengan kasus sederhana…

Misal kita memiliki tabel mhs yang berisi data mahasiswa dan tabel
mhs_status yang berisi data kelulusan mahasiswa sebagai berikut:

mhs
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |
| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
+------+---------+---------------+------------+

mhs_status
+------+-------------+
| nim | status |
+------+-------------+
| 001 | LULUS |
| 002 | TIDAK LULUS |
| 003 | TIDAK LULUS |
| 004 | LULUS |
| 005 | TIDAK LULUS |
| 006 | LULUS |
| 007 | LULUS |
+------+-------------+

Killer Trik Query MySQL 133


Selanjutnya kita akan menampilkan data kelulusan berdasarkan
jenis kelamin dengan output sebagai berikut:
+--------------+---+---+
| status_lulus | L | P |
+--------------+---+---+
| LULUS | 2 | 2 |
| TIDAK LULUS | 1 | 2 |
| TOTAL | 3 | 4 |
+--------------+---+---+

Bagaimana query nya?

Seperti yang telah kita pelajari, pertama kita akan menyusun tabel
pada klausa FROM untuk diolah lebih lanjut pada klausa SELECT.

Jika kita perhatikan tabel output, maka kita perlu data jenis kelamin
dan data kelulusan, keduanya ada di dua tabel yang berbeda, maka
kita perlu menggabungkan kedua tabel tersebut.

Penggabungan kedua tabel tersebut kita lakukan menggunakan


JOIN, sudah tahu kan kenapa pakai JOIN?

Selanjutnya, jenis JOIN yang kita gunakan adalah LEFT JOIN dan tabel
di sebelah kiri adalah tabel mhs_status, kenapa? Karena kita ingin
menampilkan semua status

Mari kita tes bentuk tabel hasil penggabungan:

1. SELECT *
2. FROM mhs
3. LEFT JOIN mhs_status USING (nim)

Hasil:
+------+-------------+---------+---------------+------------+
| nim | status | nama | jenis_kelamin | kd_jurusan |
+------+-------------+---------+---------------+------------+
| 001 | LULUS | Alfa | L | J002 |
| 002 | TIDAK LULUS | Beta | P | J002 |

134 BAB 8 Mempertajam Agregasi dan JOIN


| 003 | TIDAK LULUS | Charlie | P | J001 |
| 004 | LULUS | Delta | L | J001 |
| 005 | TIDAK LULUS | Erdhi | L | J001 |
| 006 | LULUS | Farah | P | J002 |
| 007 | LULUS | Gisel | P | J002 |
+------+-------------+---------+---------------+------------+

Dari tabel tersebut, selanjutnya kita susun klausa SELECT sehingga


menghasilkan output sesuai yang kita inginkan.

Pada tabel output, terdapat kolom baru, yaitu L dan P, kolom ini tidak
ada pada tabel hasil klausa FROM, untuk itu kita perlu membuatnya.

Bagaimana membuatnya?

Sebelum menambah kolom pada klaua SELECT, ingat kembali


latihan sebelumnya, ketika tabel output berupa penghitungan baris,
maka jika perlu kita "tarik mundur" bentuk tabel outputnya.

Jika tabel output kita tarik mundur, kita akan memperoleh tabel
seperti ini:
+-------------+------+------+
| status | L | P |
+-------------+------+------+
| LULUS | 1 | NULL |
| TIDAK LULUS | NULL | 1 |
| TIDAK LULUS | NULL | 1 |
| LULUS | 1 | NULL |
| TIDAK LULUS | 1 | NULL |
| LULUS | NULL | 1 |
| LULUS | NULL | 1 |
+-------------+------+------+

Selanjutnya, kita tambahkan kolom L dan P pada klausa SELECT


menggunakan fungsi IF. Fungsi IF ini nantinya digunakan untuk
menguji nilai tiap baris pada kolom jenis kelamin, untuk
mendapatkan nilai NULL dan 1.

Querynya adalah sebagai berikut:

Killer Trik Query MySQL 135


1. SELECT status,
2. IF(jenis_kelamin = "L", 1, NULL) AS L,
3. IF(jenis_kelamin = "P", 1, NULL) AS P
4. FROM mhs
5. LEFT JOIN mhs_status USING (nim)

Hasil:
+-------------+------+------+
| status | L | P |
+-------------+------+------+
| LULUS | 1 | NULL |
| TIDAK LULUS | NULL | 1 |
| TIDAK LULUS | NULL | 1 |
| LULUS | 1 | NULL |
| TIDAK LULUS | 1 | NULL |
| LULUS | NULL | 1 |
| LULUS | NULL | 1 |
+-------------+------+------+

Selanjutnya karena kita ingin menghitung baris, kita gunakan fungsi


COUNT. Jangan lupa menambahkan klausa GROUP BY status karena
kita ingin mengelompokkannya berdasarkan statusnya.

Bentuk query jadinya adalah sebagai berikut:

1. SELECT status,
2. COUNT(IF(jenis_kelamin = "L", 1, NULL)) AS L,
3. COUNT(IF(jenis_kelamin = "P", 1, NULL)) AS P
4. FROM mhs
5. LEFT JOIN mhs_status USING (nim)
6. GROUP BY status

Hasil:
+-------------+---+---+
| status | L | P |
+-------------+---+---+
| LULUS | 2 | 2 |
| TIDAK LULUS | 1 | 2 |
+-------------+---+---+

136 BAB 8 Mempertajam Agregasi dan JOIN


Hasil diatas sudah mendekati apa yang kita harapkan, tinggal
menambahkan baris total.

Selanjutnya, untuk menambah baris total, kita tambahkan klausa


WITH ROLLUP sebagai berikut:

1. SELECT COALESCE(status, "TOTAL") AS status_lulus,


2. COUNT(IF(jenis_kelamin = "L", 1, NULL)) AS L,
3. COUNT(IF(jenis_kelamin = "P", 1, NULL)) AS P
4. FROM mhs
5. LEFT JOIN mhs_status USING (nim)
6. GROUP BY status
7. WITH ROLLUP

Hasil:
+--------------+---+---+
| status_lulus | L | P |
+--------------+---+---+
| LULUS | 2 | 2 |
| TIDAK LULUS | 1 | 2 |
| TOTAL | 3 | 4 |
+--------------+---+---+

Pada contoh diatas, kita gunakan fungsi COALESCE untuk mengganti


nilai NULL pada baris total dengan kata TOTAL.

Fungsi COALESCE akan mencari nilai hingga ditemukan nilai


bukan NULL, fungsi ini sama dengan fungsi IFNULL(), misal
IFNULL(status, "TOTAL"), bedanya fungsi IFNULL hanya dapat
menerima dua argumen, sedangkan COALESCE bisa lebih
dari satu, misal COALESCE(status, nama, "TOTAL")

8.2. Studi Kasus #2


Pada latihan kedua ini kita akan menampilkan data nama barang
beserta jumlah yang masuk ke gudang selama bulan Mei 2017.
Tabel yang akan kita gunakan adalah sebagai berikut:

Killer Trik Query MySQL 137


barang
+-----------+-------------+------+-------+
| kd_barang | nama_barang | stok | harga |
+-----------+-------------+------+-------+
| 1 | Mouse | 14 | 76000 |
| 2 | Flashdisk | 15 | 55000 |
| 3 | Mousepad | 17 | 35000 |
| 4 | Keyboard | 12 | 80000 |
| 5 | Kabel VGA | 7 | 45000 |
+-----------+-------------+------+-------+
barang_masuk
+------------+-----------+-------------+-----------+
| tgl_masuk | kd_barang | kd_supplier | jml_masuk |
+------------+-----------+-------------+-----------+
| 2017-05-02 | 1 | 1 | 14 |
| 2017-05-03 | 1 | 2 | 5 |
| 2017-05-04 | 2 | 2 | 13 |
| 2017-05-04 | 3 | 1 | 4 |
| 2017-05-05 | 4 | 3 | 10 |
| 2017-04-26 | 1 | 1 | 7 |
+------------+-----------+-------------+-----------+

Selanjutnya kita tampilkan semua nama_barang beserta jumlah


barang masuk untuk tiap tiap jenis barang dengan tampilan
output sebagai berikut:
+-------------+-----------+
| nama_barang | jml_masuk |
+-------------+-----------+
| Mouse | 19 |
| Flashdisk | 13 |
| Mousepad | 4 |
| Keyboard | 10 |
| Kabel VGA | NULL |
+-------------+-----------+

Silakan dicoba ya….

Sudah… ?

Oke, mari kita bahas…

138 BAB 8 Mempertajam Agregasi dan JOIN


Pertama…
Seperti biasa, mari kita analisa data pada tabel output. Pada tabel
tersebut kita akan menampilkan data nama barang dan total
jml_masuk. Keduanya ada di tabel barang dan barang_masuk, untuk
itu mari kita gabungkan kedua tabel tersebut:

1. SELECT *
2. FROM barang
3. LEFT JOIN barang_masuk USING(kd_barang)

Hasil yang kita peroleh:


+-----------+-------------+------+-------+------------+-------------+-----------+
| kd_barang | nama_barang | stok | harga | tgl_masuk | kd_supplier | jml_masuk |
+-----------+-------------+------+-------+------------+-------------+-----------+
| 1 | Mouse | 14 | 76000 | 2017-05-02 | 1 | 14 |
| 1 | Mouse | 14 | 76000 | 2017-05-03 | 2 | 5 |
| 2 | Flashdisk | 15 | 55000 | 2017-05-04 | 2 | 13 |
| 3 | Mousepad | 17 | 35000 | 2017-05-04 | 1 | 4 |
| 4 | Keyboard | 12 | 80000 | 2017-05-05 | 3 | 10 |
| 1 | Mouse | 14 | 76000 | 2017-04-26 | 1 | 7 |
| 5 | Kabel VGA | 7 | 45000 | NULL | NULL | NULL |
+-----------+-------------+------+-------+------------+-------------+-----------+

Kedua…
Selanjutnya kita definisikan kolom pada klausa SELECT. Jika
memperhatikan tabel output, maka kolom yang akan kita gunakan
adalah nama_barang dan jml_masuk (total per jenis barang) selain
itu, data yang ditampilkan hanya data bulan mei 2017

Maka, query yang kita perlukan adalah:

1. SELECT nama_barang, SUM(jml_masuk) jml_masuk


2. FROM barang
3. LEFT JOIN barang_masuk USING(kd_barang)
4. WHERE (YEAR(tgl_masuk) = 2017 AND MONTH(tgl_masuk) = 5)
5. GROUP BY kd_barang

Killer Trik Query MySQL 139


Perhatikan bahwa pada query diatas kita tambahkan klausa WHERE
untuk mendapatkan data bulan Mei 2017.

Hasil yang kita peroleh adalah:


+-------------+-----------+
| nama_barang | jml_masuk |
+-------------+-----------+
| Mouse | 19 |
| Flashdisk | 13 |
| Mousepad | 4 |
| Keyboard | 10 |
+-------------+-----------+

Yes, behasil..…

Eitss, Tapi tunggu dulu…..


Adakah anda melihat keanehan disana?

Yup, ternyata terdapat data barang yang hilang, yaitu kabel VGA.
Kenapa bisa begitu?

Coba perhatikan tabel penggabungan pada bagian pertama.


Ternyata kita dapati bahwa untuk kabel VGA, data pada kolom
barang_masuk (tgl_masuk, kd_supplier, dan jml_masuk) nilainya
NULL semua.

Selanjutnya, pada klausa WHERE kita beri filter tahun dan bulan pada
tgl_masuk yang mengakibatkan data tgl_masuk yang bernilai NULL
tidak masuk dalam kriteria sehingga data tersebut tidak ditampilkan.

Bagaimana mengatasinya?

Caranya, kita tambahkan kriteria pada klausa WHERE sehingga data


dengan tgl_masuk NULL ikut masuk, ubah querynya menjadi seperti
ini:

140 BAB 8 Mempertajam Agregasi dan JOIN


1. SELECT nama_barang, SUM(jml_masuk) jml_masuk
2. FROM barang
3. LEFT JOIN barang_masuk USING(kd_barang)
5. WHERE (YEAR(tgl_masuk) = 2017 AND MONTH(tgl_masuk) = 5) OR
6. tgl_masuk IS NULL
7. GROUP BY kd_barang
8.

Hasilnya adalah:
+-------------+-----------+
| nama_barang | jml_masuk |
+-------------+-----------+
| Mouse | 19 |
| Flashdisk | 13 |
| Mousepad | 4 |
| Keyboard | 10 |
| Kabel VGA | NULL |
+-------------+-----------+

Yes… berhasil

Sekali lagi, untuk menguji nilai NULL, kita tidak bisa


menggunakan operator = atau != seperti tgl_masuk = NULL
melainkan kita harus menggunakan IS atau IS NOT

Perhatikan pada contoh diatas, kita menggunakan tanda kurung


pada klausa WHERE, kenapa? Ingat kembali pembahasan tentang
operator OR dan AND.

Jika tanpa tanda kurung, maka kondisi yang dievaluasi adalah:

1. Tahun 2017 dan bulan 5: YEAR(tgl_masuk) = 2017 AND


MONTH(tgl_masuk)

2. Dan bulan 5 atau tgl_masuk NULL: MONTH(tgl_masuk) = 5) OR


tgl_masuk IS NULL

Sedangkan dengan tanda kurung, filter menjadi:

Killer Trik Query MySQL 141


1. Tahun 2017 dan bulan 5 atau tgl_masuk NULL

Ingat kembali, pada operator AND dan OR, maka yang


pertama dievaluasi adalah AND baru kemudian OR,
namun jika ada tanda kurung, maka pertama kali yang
dievaluasi adalah yang ada di dalam tanda kurung

Pada klausa WHERE jika menggunakan operator AND dan


OR bersama sama, selalu gunakan tanda kurung untuk
membuat prioritas kondisi yang ingin dievaluasi.

8.3. Studi Kasus #3


Untuk menguji pemahaman Anda tentang apa yang telah kita bahas
sejauh ini, pada studi kasus kali ini kita akan menggunakan tiga tabel
sekaligus yaitu tabel mhs, mhs_status, dan mhs_jurusan sebagai
berikut:
mhs
+------+---------+---------------+------------+
| nim | nama | jenis_kelamin | kd_jurusan |
+------+---------+---------------+------------+
| 001 | Alfa | L | J002 |
| 002 | Beta | P | J002 |
| 003 | Charlie | P | J001 |
| 004 | Delta | L | J001 |
| 005 | Erdhi | L | J001 |
| 006 | Farah | P | J002 |
| 007 | Gisel | P | J002 |
| 008 | Haris | L | NULL |
+------+---------+---------------+------------+
mhs_status
+------+-------------+
| nim | status |
+------+-------------+
| 001 | LULUS |
| 002 | TIDAK LULUS |
| 003 | TIDAK LULUS |
| 004 | LULUS |
| 005 | TIDAK LULUS |
| 006 | LULUS |

142 BAB 8 Mempertajam Agregasi dan JOIN


| 007 | LULUS |
+------+-------------+
mhs_jurusan
+------------+--------------+
| kd_jurusan | nama_jurusan |
+------------+--------------+
| J001 | MANAJEMEN |
| J002 | AKUNTANSI |
| J003 | TEKNIK |
+------------+--------------+

Selanjutnya kita akan menampilkan data status (LULUS dan TIDAK


LULUS) yang dikelompokkan berdasarkan jurusan dan jenis kelamin,
tabel outputnya adalah sebagai berikut:
+-------------+-------------+-------------+-------------+-------------+
| status | manajemen_l | manajemen_p | akuntansi_l | akuntansi_p |
+-------------+-------------+-------------+-------------+-------------+
| LULUS | 1 | 0 | 1 | 2 |
| TIDAK LULUS | 1 | 1 | 0 | 1 |
+-------------+-------------+-------------+-------------+-------------+

Sebelum lanjut, sebagiknya Anda coba terlebih dahulu sambil


berlatih, paling membutuhkan waktu 5-10 menit, practice make
perfect, right?

Sudah…?

Oke, mari kita bahas….

Pertama…
Seperti prinsip yang telah kita bahas, pertama kita analisa tabel
output. Pada tabel tersebut terdapat data jumlah jenis kelamin,
status, dan jurusan, ketiganya ada di ketiga tabel, untuk itu, kita
perlu menggabungkan ketiga tabel.

Kali ini jenis penggabungan yang kita gunakan adalah LEFT JOIN dan
kita tempatkan tabel mhs di paling kiri. Mari kita tes hasil
penggabungan menggunakan statemen SELECT sebagai berikut

Killer Trik Query MySQL 143


1. SELECT *
2. FROM mhs
3. LEFT JOIN mhs_status USING (nim)
4. LEFT JOIN mhs_jurusan USING (kd_jurusan)

Hasil yang kita peroleh:


+------------+------+---------+---------------+-------------+--------------+
| kd_jurusan | nim | nama | jenis_kelamin | status | nama_jurusan |
+------------+------+---------+---------------+-------------+--------------+
| J001 | 003 | Charlie | P | TIDAK LULUS | MANAJEMEN |
| J001 | 004 | Delta | L | LULUS | MANAJEMEN |
| J001 | 005 | Erdhi | L | TIDAK LULUS | MANAJEMEN |
| J002 | 001 | Alfa | L | LULUS | AKUNTANSI |
| J002 | 002 | Beta | P | TIDAK LULUS | AKUNTANSI |
| J002 | 006 | Farah | P | LULUS | AKUNTANSI |
| J002 | 007 | Gisel | P | LULUS | AKUNTANSI |
| NULL | 008 | Haris | L | NULL | NULL |
+------------+------+---------+---------------+-------------+--------------+

Apakah sudah terbayang query SELECT yang akan kita jalankan?

Sudah kan? Ya, karena kondisi pada kasus #3 ini mirip dengan kasus
#2

Kedua…
Ok, mari kita susun kolom pada klausa SELECT nya.

Jika kita analisa tabel output, terdapat kolom baru yaitu


manajemen_l, namajemen_p, akuntansi_l, dan akuntansi_p. Selain itu
kita akan menghitung jumlah baris, untuk itu, tabel yang kita
perlukan adalah:
+-------------+-------------+-------------+-------------+-------------+
| status | manajemen_l | manajemen_p | akuntansi_l | akuntansi_p |
+-------------+-------------+-------------+-------------+-------------+
| TIDAK LULUS | NULL | 1 | NULL | NULL |
| LULUS | 1 | NULL | NULL | NULL |
| TIDAK LULUS | 1 | NULL | NULL | NULL |
| LULUS | NULL | NULL | 1 | NULL |
| TIDAK LULUS | NULL | NULL | NULL | 1 |
| LULUS | NULL | NULL | NULL | 1 |
| LULUS | NULL | NULL | NULL | 1 |
| NULL | NULL | NULL | NULL | NULL |
+-------------+-------------+-------------+-------------+-------------+

144 BAB 8 Mempertajam Agregasi dan JOIN


Bagaimana querynya?

Masih ingat kaidah penggunaan fungsi IF ? Nah, kali ini sama seperti
kasus #2, kita perlu menguji nilai baris satu per satu apakah:

 Nama jurusan manajemen dan jenis kelamin L, jika ya, beri nilai
1, jika tidak, beri nilai NULL dan letakkan pada kolom pertama

 Nama jurusan manajemen dan jenis kelamin P, jika ya, beri nilai
1, jika tidak, beri nilai NULL dan letakkan pada kolom kedua

 Nama jurusan akuntansi dan jenis kelamin L, jika ya, beri nilai 1,
jika tidak, beri nilai NULL dan letakkan pada kolom ketiga

 Nama jurusan akuntansi dan jenis kelamin P, jika ya, beri nilai 1,
jika tidak, beri nilai NULL dan letakkan pada kolom keempat

Setelah kita tahu alur logikanya, mari kita susun querynya…

Querynya adalah sebagai berikut:

1. SELECT status,
2. IF( jenis_kelamin = "L" AND kd_jurusan = "J001"
3. , 1, NULL) AS manajemen_l,
4. IF( jenis_kelamin = "P" AND kd_jurusan = "J001"
5. , 1, NULL) AS manajemen_p,
6. IF( jenis_kelamin = "L" AND kd_jurusan = "J002"
7. , 1, NULL) AS akuntansi_l,
8. IF( jenis_kelamin = "P" AND kd_jurusan = "J002"
9. , 1, NULL) AS akuntansi_p
10. FROM mhs_status
11. LEFT JOIN mhs USING (nim)
12. LEFT JOIN mhs_jurusan USING (kd_jurusan)

Selanjutnya kita tambahkan fungsi COUNT dan klausa GROUP BY


status sebagai berikut:

1. SELECT status,
2. COUNT(IF( jenis_kelamin = "L" AND kd_jurusan = "J001"

Killer Trik Query MySQL 145


3. , 1, NULL)) AS manajemen_l,
4. COUNT(IF( jenis_kelamin = "P" AND kd_jurusan = "J001"
5. , 1, NULL)) AS manajemen_p,
6. COUNT(IF( jenis_kelamin = "L" AND kd_jurusan = "J002"
7. , 1, NULL)) AS akuntansi_l,
8. COUNT(IF( jenis_kelamin = "P" AND kd_jurusan = "J002"
9. , 1, NULL)) AS akuntansi_p
10. FROM mhs_status
11. LEFT JOIN mhs USING (nim)
12. LEFT JOIN mhs_jurusan USING (kd_jurusan)
13. GROUP BY status

Query diatas sama denan studi kasus #2, bedanya kita hanya
menambahkan kondisi pada fungsi IF dengan AND jd_jurusan... dan
kita join kan tabel mhs_jurusan

Hasil yang kita peroleh adalah:


+-------------+-------------+-------------+-------------+-------------+
| status | manajemen_l | manajemen_p | akuntansi_l | akuntansi_p |
+-------------+-------------+-------------+-------------+-------------+
| NULL | 0 | 0 | 0 | 0 |
| LULUS | 1 | 0 | 1 | 2 |
| TIDAK LULUS | 1 | 1 | 0 | 1 |
+-------------+-------------+-------------+-------------+-------------+

Yes! berhasil

Tapi tunggu, apakah Anda melihat keanehan?

Ya!!, ternyata ada status dengan nilai NULL !!

Mengapa demikian? Hal ini disebabkan karena terdapat data


mahasiswa yang tidak ada di tabel mhs_status masih ingat kaidah
pada LEFT JOIN kan?

Bagaimana mengatasinya? Caranya tinggal dibalik posisi tabel nya,


sehingga tabel mhs_status berada di sebelah kiri dan tabel mhs
berada di sebelah kanan, sehingga querynya menjadi:

146 BAB 8 Mempertajam Agregasi dan JOIN


1. SELECT ...
2. FROM mhs_status
3. LEFT JOIN mhs USING (nim)
4. LEFT JOIN mhs_jurusan USING (kd_jurusan)
5. GROUP BY status

Ingat kembali: Karena kita selalu menggunakan LEFT JOIN,


maka tempatkan tabel yang ingin kita tampilkan semua
datanya di sebelah kiri. Pada contoh diatas karena kita ingin
menampilkan semua status, maka kita tempatkan tabel
mhs_status di paling kiri

Selain cara diatas, terdapat cara lain untuk menghilangkan baris


NULL yaitu menggunakan klausa WHERE sebagai berikut:

1. SELECT ...
2. FROM mhs
3. LEFT JOIN mhs_status USING (nim)
4. LEFT JOIN mhs_jurusan USING (kd_jurusan)
5. WHERE status IS NOT NULL
6. GROUP BY status

Pada contoh diatas, kita menggunakan IS NOT NULL

Namun demikian, agar selalu konsisten, selalu tempatkan tabel yang


ingin ditampilkan semua datanya di sebelah kiri.

Ingat kembali: NULL tidak bisa menggunakan operator


apapun, hanya bisa menggunakan IS NULL atau IS NOT
NULL

8.4. Studi Kasus #4


Pada studi kasus kali ini, kita akan menguji pemahaman Anda
mengenai fungsi agregasi, fungsi IF, dan Join.

Killer Trik Query MySQL 147


Misal dari tabel mahasiswa yang digunakan pada studi kasus #3,
yaitu mhs, mhs_jurusan, dan mhs_status kita akan membuat resume
jumlah mahasiswa yang lulus dan yang tidak lulus yang
dikelompokkan per jurusan, beserta persentasenya.

Tabel output yang kita inginkan adalah sebagai berikut:


+--------------+------------------+-------+-------------+
| Nama Jurusan | Jumlah Mahasiswa | Lulus | Tidak Lulus |
+--------------+------------------+-------+-------------+
| MANAJEMEN | 3 | 1 | 2 |
| AKUNTANSI | 4 | 3 | 1 |
| TEKNIK | 0 | 0 | 0 |
+--------------+------------------+-------+-------------+

Bagaimana querynya?

Pertama…
Kita buat hubungan ketiga tabel menggunakan JOIN dan kita
tempatkan tabel mhs_jurusan di paling kiri, kenapa? Karena data
nama jurusan akan kita tampilkan semua. Selanjutnya kita tes hasil
penggabungan nya, jalankan query berikut:

1. SELECT *
2. FROM mhs_jurusan
3. LEFT JOIN mhs USING (kd_jurusan)
4. LEFT JOIN mhs_status USING (nim)

Hasil:
+------+------------+--------------+---------+---------------+-------------+
| nim | kd_jurusan | nama_jurusan | nama | jenis_kelamin | status |
+------+------------+--------------+---------+---------------+-------------+
| 001 | J002 | AKUNTANSI | Alfa | L | LULUS |
| 002 | J002 | AKUNTANSI | Beta | P | TIDAK LULUS |
| 003 | J001 | MANAJEMEN | Charlie | P | TIDAK LULUS |
| 004 | J001 | MANAJEMEN | Delta | L | LULUS |
| 005 | J001 | MANAJEMEN | Erdhi | L | TIDAK LULUS |
| 006 | J002 | AKUNTANSI | Farah | P | LULUS |
| 007 | J002 | AKUNTANSI | Gisel | P | LULUS |
| NULL | J003 | TEKNIK | NULL | NULL | NULL |
+------+------------+--------------+---------+---------------+-------------+

148 BAB 8 Mempertajam Agregasi dan JOIN


Kedua…
Kedua, kita susun kolom pada klausa SELECT, namun sebelumnya
kita analisa terlebih dahulu kolom pada tabel output dengan
memperhatikan tabel hasil eksekusi klausa FROM:

1. Nama Jurusan. Kolom ini sudah ada pada tabel hasil klausa
FROM, sehingga tinggal kita tuliskan pada klausa SELECT

2. Jumlah Mahasiswa. Kolom ini belum ada, sehingga kita perlu


membuatnya. Nilai kolom ini kita dapatkan dengan menghitung
banyaknya NIM

3. Lulus. Kolom ini juga belum ada, sehingga kita perlu


membuatnya. Nilai kolom ini kita ambil dari banyaknya nim
dengan status lulus

4. Tidak Lulus. Kolom ini juga belum ada, sehingga kita perlu
membuatnya. Nilai kolom ini kita ambil dari banyaknya nim
dengan status tidak lulus

Dari hasil analisa diatas, susunan kolom pada klausa SELECT adalah
sebagai berikut:

1. SELECT nama_jurusan AS "Nama Jurusan"


2. , nim AS "Jumlah Mahasiswa"
3. , IF(status="lulus", nim, NULL) AS "Lulus"
4. , IF(status="tidak lulus", nim, NULL) AS "Tidak Lulus"
5. FROM mhs_jurusan
LEFT JOIN mhs USING(kd_jurusan)
6. LEFT JOIN mhs_status USING(nim)
7.

Hasil:
+--------------+------------------+-------+-------------+
| Nama Jurusan | Jumlah Mahasiswa | Lulus | Tidak Lulus |
+--------------+------------------+-------+-------------+

Killer Trik Query MySQL 149


| AKUNTANSI | 001 | 001 | NULL |
| AKUNTANSI | 002 | NULL | 002 |
| MANAJEMEN | 003 | NULL | 003 |
| MANAJEMEN | 004 | 004 | NULL |
| MANAJEMEN | 005 | NULL | 005 |
| AKUNTANSI | 006 | 006 | NULL |
| AKUNTANSI | 007 | 007 | NULL |
| TEKNIK | NULL | NULL | NULL |
+--------------+------------------+-------+-------------+

Selanjutnya kita hitung banyaknya nim dengan menggunakan fungsi


COUNT. Jangan lupa ya, karena kita akan menghitung berdasarkan
nama jurusan, maka kita tambahkan klausa GROUP BY jurusan.

Querynya adalah sebagai berikut:

1. SELECT nama_jurusan AS "Nama Jurusan"


2. , COUNT(nim) AS "Jumlah Mahasiswa"
3. , COUNT(IF(status="lulus", nim, NULL)) AS "Lulus"
4. , COUNT(IF(status="tidak lulus", nim, NULL)) AS "Tidak
5. Lulus"
FROM mhs_jurusan
6. LEFT JOIN mhs USING(kd_jurusan)
7. LEFT JOIN mhs_status USING(nim)
8. GROUP BY kd_jurusan

Hasil:
+--------------+------------------+-------+-------------+
| Nama Jurusan | Jumlah Mahasiswa | Lulus | Tidak Lulus |
+--------------+------------------+-------+-------------+
| MANAJEMEN | 3 | 1 | 2 |
| AKUNTANSI | 4 | 3 | 1 |
| TEKNIK | 0 | 0 | 0 |
+--------------+------------------+-------+-------------+

Pada contoh diatas, kolom alias mengandung spasi, sehingga kita


harus menggunakan tanda kutip.

Pada operator pembanding, dalam contoh diatas adalah =,


maka data yang dibandingkan secara default bersifat case
insensitive jadi lulus dan LULUS sama saja. Hal ini tergantung

150 BAB 8 Mempertajam Agregasi dan JOIN


dari collation tabel atau kolom (agar pembahasan tidak terlalu
melebar, kita tidak membahas collation dibuku ini)

Pengayaan….
Selanjutnya, untuk lebih mengasah kemampuan Anda, silakan
kembangkan tabel diatas dengan menambahkan kolom persentase
sebagai berikut:
+--------------+------------------+-------+---------+-------------+---------------+
| Nama Jurusan | Jumlah Mahasiswa | Lulus | % Lulus | Tidak Lulus | % Tidak Lulus |
+--------------+------------------+-------+---------+-------------+---------------+
| MANAJEMEN | 3 | 1 | 33.33% | 2 | 66.67% |
| AKUNTANSI | 4 | 3 | 75.00% | 1 | 25.00% |
| TEKNIK | 0 | 0 | NULL | 0 | NULL |
+--------------+------------------+-------+---------+-------------+---------------+

Ayo dicoba…

Sudah bisa.. ?

Ok, mari kita bahas. Nilai persentase diperoleh dengan


membandingkan antara jumlah mahasiswa yang lulus / tidak lulus
dengan jumlah keseluruhan mahasiswa, sehingga kita gunakan
operator aritmetika biasa. Mari kita ubah query kita menjadi:

1. SELECT nama_jurusan AS "Nama Jurusan"


2. , COUNT(nim) AS "Jumlah Mahasiswa"
3. , COUNT(IF(status="lulus", nim, NULL)) AS "Lulus"
4. , CONCAT ( ROUND( COUNT(IF(status="lulus", nim, NULL))
/ COUNT(nim) * 100, 2), '%') AS "% Lulus"
5. , COUNT(IF(status="tidak lulus", nim, NULL)) AS "Tidak
Lulus"
6. , CONCAT ( ROUND( COUNT(IF(status="tidak lulus", nim,
NULL)) / COUNT(nim) * 100, 2), '%') AS "% Tidak Lulus"
7. FROM mhs_jurusan
8. LEFT JOIN mhs USING(kd_jurusan)
9. LEFT JOIN mhs_status USING(nim)
10. GROUP BY kd_jurusan

Pada query diatas, terdapat tambahan query pada baris 4 dan 6.


Pada query tersebut kita menggunakan fungsi ROUND untuk

Killer Trik Query MySQL 151


membulatkan desimal persen hingga 2 angka di belakang koma,
selain itu kita gunakan fungsi CONCAT untuk menggabungkan
persentase dengan tanda persen ( % )

Ingat kembali: gunakan CONCAT untuk menggabungkan nilai


kolom maupun string dan ROUND untuk membulatkan nilai
desimal.

8.5. Studi Kasus #5


Pada kasus kali ini kita akan membuat resume jumlah guru yang
mengikuti pelatihan per kabupaten. Adapun tabel yang tersedia dan
hubungan antar tabel adalah sebagai berikut:

152 BAB 8 Mempertajam Agregasi dan JOIN


Gambar 8.1 Hubungan Antar Tabel

Sedangkan tabel output yang akan ditampilkan adalah sebagai


berikut:

Killer Trik Query MySQL 153


+----------------+----+-----+-----+-------+
| Nama Kabupaten | SD | SMP | SMA | TOTAL |
+----------------+----+-----+-----+-------+
| Kabupaten 1 | 1 | 2 | 0 | 3 |
| Kabupaten 2 | 0 | 1 | 2 | 3 |
| Kabupaten 3 | 1 | 1 | 0 | 2 |
| Kabupaten 4 | 0 | 0 | 0 | 0 |
+----------------+----+-----+-----+-------+

Silakan Anda coba ya.. sudah banyak contoh seperti ini pada latihan
sebelumnya… practice make perfect…. Benar?

Sudah bisa?

Oke mari kita bahas….

Pertama…
Seperti biasa, pertama kita analisa tabel yang diperlukan dengan
melihat output tabel.

Pada output tabel, kita akan menghitung data guru yang ada pada
tabel sekolah_guru dengan mengelompokkannya per kabupaten
(tabel sekolah_kab), berdasarkan hubungan gambar diatas, maka
mau tidak mau kita harus melibatkan keempat tabel, karena agar
tabel sekolah_kab dapat terhubung dengan sekolah_guru, kita harus
melibatkan tabel sekolah dan sekolah_kec

Sekarang kita gabungkan semua tabel menggunakan join dan kita


tes hasil penggabungan tersebut dengan klausa SELECT. Agar hasil
uji coba tidak terlalu banyak kolomnya, kali ini kita tidak
menggunakan asterik *, melainkan kita pilih kolom yang akan
digunakan saja yaitu kolom nama_guru, jenjang, pelatihan, dan nama
kabupaten.

Jalankan query berikut:

154 BAB 8 Mempertajam Agregasi dan JOIN


1. SELECT nama_guru, jenjang, pelatihan, nama_kab
2. FROM sekolah_guru
3. LEFT JOIN sekolah USING(kd_sekolah)
4. LEFT JOIN sekolah_kec USING(kd_kec)
5. LEFT JOIN sekolah_kab USING(kd_kab)

Hasil yang kita peroleh:


+-----------+---------+-----------+-------------+
| nama_guru | jenjang | pelatihan | nama_kab |
+-----------+---------+-----------+-------------+
| Budi | SD | | Kabupaten 1 |
| Jeni | SMP | IKUT | Kabupaten 1 |
| Hari | SD | | Kabupaten 1 |
| Indri | SMP | IKUT | Kabupaten 1 |
| Erdi | SD | IKUT | Kabupaten 1 |
| Ferry | SMA | IKUT | Kabupaten 2 |
| Gari | SMA | IKUT | Kabupaten 2 |
| Denny | SMP | IKUT | Kabupaten 2 |
| Aldi | SD | IKUT | Kabupaten 3 |
| Cyndi | SMP | IKUT | Kabupaten 3 |
+-----------+---------+-----------+-------------+

Sudah benar?

Wait… coba perhatikan kolom nama_kab, ternyata ada yang kurang,


tahu kurangnya dimana? Yup, Kabupaten 4 tidak ada.

Kenapa begitu?

Seperti sebelum sebelumnya, karena kita kita akan menampilkan


semua data nama kabupaten beserta jumlah guru yang mengikuti
pelatihan, maka tabel yang paling kiri adalah tabel sekolah_kab.
Jangan terkecoh dengan urutan tabel pada gambar atau urutan
tabel berdasarkan abjad, karena meskipun tampak rapi, belum tentu
hasilnya benar.

Mari kita ubah querynya:

Killer Trik Query MySQL 155


1. SELECT nama_kab, nama_guru, jenjang, pelatihan
2. FROM sekolah_kab
3. LEFT JOIN sekolah_kec USING(kd_kab)
4. LEFT JOIN sekolah USING(kd_kec)
5. LEFT JOIN sekolah_guru USING(kd_sekolah)

Hasil:
+-------------+-----------+---------+-----------+
| nama_kab | nama_guru | jenjang | pelatihan |
+-------------+-----------+---------+-----------+
| Kabupaten 3 | Aldi | SD | IKUT |
| Kabupaten 1 | Budi | SD | |
| Kabupaten 3 | Cyndi | SMP | IKUT |
| Kabupaten 2 | Denny | SMP | IKUT |
| Kabupaten 1 | Erdi | SD | IKUT |
| Kabupaten 2 | Ferry | SMA | IKUT |
| Kabupaten 2 | Gari | SMA | IKUT |
| Kabupaten 1 | Hari | SD | |
| Kabupaten 1 | Indri | SMP | IKUT |
| Kabupaten 1 | Jeni | SMP | IKUT |
| Kabupaten 4 | NULL | NULL | NULL |
+-------------+-----------+---------+-----------+

Apakah hasilnya sudah benar? Yes, sudah!

Selanjutnya kita melangkah ke bagian kedua

Kedua…
Kedua mari kita susun kolom pada klausa SELECT. Bagian Kolom
Nama kabupaten sudah tersedia, kita tinggal menuliskannya pada
klausa SELECT, untuk kolom SD, SMP, SMA, dan TOTAL, kita perlu
membuatnya. Jalankan query berikut:

1. SELECT nama_kab AS "Nama Kabupaten"


2. , IF(jenjang="SD" AND pelatihan = "IKUT", 1, NULL) AS SD
, IF(jenjang="SMP" AND pelatihan = "IKUT", 1, NULL) AS SMP
3. , IF(jenjang="SMA" AND pelatihan = "IKUT", 1, NULL) AS SMA
, IF(pelatihan = "IKUT",1,NULL) AS TOTAL
4. FROM sekolah_kab
LEFT JOIN sekolah_kec USING(kd_kab)

156 BAB 8 Mempertajam Agregasi dan JOIN


5. LEFT JOIN sekolah USING(kd_kec)
6. LEFT JOIN sekolah_guru USING(kd_sekolah)

Hasil:
+----------------+------+------+------+-------+
| Nama Kabupaten | SD | SMP | SMA | TOTAL |
+----------------+------+------+------+-------+
| Kabupaten 3 | 1 | NULL | NULL | 1 |
| Kabupaten 1 | NULL | NULL | NULL | NULL |
| Kabupaten 3 | NULL | 1 | NULL | 1 |
| Kabupaten 2 | NULL | 1 | NULL | 1 |
| Kabupaten 1 | 1 | NULL | NULL | 1 |
| Kabupaten 2 | NULL | NULL | 1 | 1 |
| Kabupaten 2 | NULL | NULL | 1 | 1 |
| Kabupaten 1 | NULL | NULL | NULL | NULL |
| Kabupaten 1 | NULL | 1 | NULL | 1 |
| Kabupaten 1 | NULL | 1 | NULL | 1 |
| Kabupaten 4 | NULL | NULL | NULL | NULL |
+----------------+------+------+------+-------+

Selanjutnya kita hitung jumlah baris untuk kolom SD, SMP, dan SMA
menggunakan fungsi COUNT. Jangan lupa karena kita akan
mengelompokkan data berdasarkan nama kabupaten, maka kita
tambahkan klausa GROUP BY kd_kab

Query jadinya adalah sebagai berikut:

1. SELECT nama_kab AS "Nama Kabupaten"


2. , COUNT(IF(jenjang="SD" AND pelatihan = "IKUT", 1,
NULL)) AS SD
3. , COUNT(IF(jenjang="SMP" AND pelatihan = "IKUT", 1,
NULL)) AS SMP
4. , COUNT(IF(jenjang="SMA" AND pelatihan = "IKUT", 1,
NULL)) AS SMA
5. , COUNT(IF(pelatihan = "IKUT",1,NULL)) AS TOTAL
6. FROM sekolah_kab
7. LEFT JOIN sekolah_kec USING(kd_kab)
8. LEFT JOIN sekolah USING(kd_kec)
9. LEFT JOIN sekolah_guru USING(kd_sekolah)
10. GROUP BY kd_kab

Hasil:

Killer Trik Query MySQL 157


+----------------+----+-----+-----+-------+
| Nama Kabupaten | SD | SMP | SMA | TOTAL |
+----------------+----+-----+-----+-------+
| Kabupaten 1 | 1 | 2 | 0 | 3 |
| Kabupaten 2 | 0 | 1 | 2 | 3 |
| Kabupaten 3 | 1 | 1 | 0 | 2 |
| Kabupaten 4 | 0 | 0 | 0 | 0 |
+----------------+----+-----+-----+-------+

Sudah benar? Yup! Sudah benar….

Cara lain…
Query adalah seni sehingga banyak jalan untuk memecahkan kasus,
demikian juga dengan kasus diatas, kasus diatas juga dapat di
selesaikan menggunakan query berikut:

1. SELECT nama_kab AS "Nama Kabupaten"


2. , COUNT(IF(jenjang="SD", 1, NULL)) AS SD
3. , COUNT(IF(jenjang="SMP", 1, NULL)) AS SMP
4. , COUNT(IF(jenjang="SMA", 1, NULL)) AS SMA
5. , COUNT(pelatihan) AS TOTAL
6. FROM sekolah_kab
7. LEFT JOIN sekolah_kec USING(kd_kab)
8. LEFT JOIN sekolah USING(kd_kec)
9. LEFT JOIN sekolah_guru USING(kd_sekolah)
10. WHERE pelatihan = "IKUT" OR nama_guru IS NULL
11. GROUP BY kd_kab

Karena pada fungsi IF terdapat kondisi yang sama, yaitu pelatihan =


"IKUT" maka kondisi ini dapat kita tempatkan pada klusa WERE

158 BAB 8 Mempertajam Agregasi dan JOIN


BAB 9 Menguasai SUBQUERY
Subquery artinya query didalam query atau klausa SELECT didalam
statemen SELECT

Subquery ini merupakan fitur yang sangat powerfull untuk


memecahkan berbagai permasalahan query, untuk itu Anda perlu
memahami konsep ini dengan baik, dan ingat: practice.. practice..
practice…

9.1. Subquery Sebagai Kolom


Model pertama dari subquery adalah subquery sebagai kolom.
Dinamakan demikian karena subquery ini letaknya pada klausa
SELECT. Ingat pembahasan kita pada awal bab SELECT, bahwa
semua yang ada pada klausa select akan ditampilkan sebagai kolom.

9.1.1 Studi Kasus #1


Misal kita memiliki tabel anggota_dewan dengan data sebagai
berikut:
+------------+--------------+-------------+-----------+
| id_anggota | nama_anggota | nama_partai | periode |
+------------+--------------+-------------+-----------+
| 1 | Alfa | Rakyat | 2010-2015 |
| 2 | Beta | Rakyat | 2015-2020 |
| 3 | Charlie | Rakyat | 2010-2015 |
| 4 | Denny | PRDD | 2015-2020 |
| 5 | Erry | PRDD | 2010-2015 |
| 6 | Ferry | PRDD | 2015-2020 |
+------------+--------------+-------------+-----------+

Selanjutnya kita akan menampilkan data jumlah anggota


berdasarkan nama partai dan periode sebagai berikut:

Killer Trik Query MySQL 159


+-------------+--------+--------+-----------+
| nama_partai | jumlah | persen | periode |
+-------------+--------+--------+-----------+
| Rakyat | 2 | 66.67 | 2010-2015 |
| PRDD | 1 | 33.33 | 2010-2015 |
| Rakyat | 1 | 33.33 | 2015-2020 |
| PRDD | 2 | 66.67 | 2015-2020 |
+-------------+--------+--------+-----------+

Bagaimana query yang kita jalankan? Pertama kita buat tanpa


persentase, query yang kita jalankan:

1. SELECT nama_partai,
2. COUNT(nama_anggota) AS jumlah_anggota,
3. periode
4. FROM anggota_dewan
5. GROUP BY nama_partai, periode
6. ORDER BY periode

Hasil:
+-------------+----------------+-----------+
| nama_partai | jumlah_anggota | periode |
+-------------+----------------+-----------+
| Rakyat | 3 | 2010-2015 |
| PRDD | 1 | 2010-2015 |
| Rakyat | 1 | 2015-2020 |
| PRDD | 2 | 2015-2020 |
+-------------+----------------+-----------+

Selanjutnya, persentase kita hitung dengan membagi jumlah


anggota dengan seluruh jumlah anggota pada periode yang sama,
nah untuk mendapatkan jumlah seluruh anggota per periode, kita
harus menggunakan data periode pada tabel utama, query yang kita
jalankan:

1. SELECT nama_partai,
2. COUNT(nama_partai) AS jumlah,
3. ROUND( COUNT(nama_partai)
4. / (SELECT COUNT(*)
5. FROM anggota_dewan
6. WHERE periode = t1.periode) * 100, 2

160 9.1. Subquery Sebagai Kolom


7. ) AS persen,
8. periode
9. FROM anggota_dewan t1
10. GROUP BY nama_partai, periode
11. ORDER BY periode

Hasil:
+-------------+--------+--------+-----------+
| nama_partai | jumlah | persen | periode |
+-------------+--------+--------+-----------+
| Rakyat | 3 | 75.00 | 2010-2015 |
| PRDD | 1 | 25.00 | 2010-2015 |
| Rakyat | 1 | 33.33 | 2015-2020 |
| PRDD | 2 | 66.67 | 2015-2020 |
+-------------+--------+--------+-----------+

Pada query diatas, klausa SELECT akan dieksekusi pada setiap baris
tabel anggota_dewan sehingga klausa WHERE periode = t1.periode
pada subquery akan berubah ubah sesuai dengan baris yang ada,
misal pada baris pertama, klausa where menjadi WHERE periode =
2010-2015, sedangkan pada baris ke 3 WHERE periode = 2015-2020

Perhatikan ilustrasi berikut:

Gambar 9.1 Ilustrasi Perubahan Klausa WHERE Pada Correlatd Subquery

Subquery ini sering digunakan ketika membuat persentase


dimana penyebutnya merupakan total dari suatu data.

Killer Trik Query MySQL 161


Correlated Subquery
Model subquery ditas berbentuk Correlated subquery (subquery
berkorelasi) artinya subquery yang bergantung pada nilai kolom
yang ada pada query utama.

Bagaimana mengidentifikasi masalah sehingga kita menggunakan


correlated subquery?

Correlated Subquery terjadi jika subquery yang kita buat


mengandung klausa WHERE, dan kondisi pada klausa WHERE ini
melibatkan kolom pada query utama.

Bagaimana kita tahu kondisi pada klausa WHERE ini melibatkan


query utama? Untuk menemukannya, mau tidak mau kita harus bisa
mengidentifikasi permasalahan yang ingin kita pecahkan.

9.2 Subquery Sebagai Data


Subquery sebagai data artinya bahwa subquery diletakkan pada
klausa FROM, sehingga menghasilkan tabel, nah tabel ini yang
dimaksud dengan data.

Untuk memahami model subquery ini, mari kita belajar dari contoh
kasus.

9.2.1. Studi Kasus #2


Pada studi kasus ini, kita ambil contoh yang paling mudah. Misal kita
memiliki tabel barang, barang_masuk, dan barang_keluar sebagai
berikut:

162 9.1. Subquery Sebagai Kolom


barang
+-----------+-------------+------+-------+
| kd_barang | nama_barang | stok | harga |
+-----------+-------------+------+-------+
| 1 | Mouse | 14 | 76000 |
| 2 | Flashdisk | 15 | 55000 |
| 3 | Mousepad | 17 | 35000 |
| 4 | Keyboard | 12 | 80000 |
| 5 | Kabel VGA | 7 | 45000 |
+-----------+-------------+------+-------+
barang_masuk
+------------+-----------+-------------+-----------+
| tgl_masuk | kd_barang | kd_supplier | jml_masuk |
+------------+-----------+-------------+-----------+
| 2017-05-02 | 1 | 1 | 14 |
| 2017-05-03 | 1 | 2 | 5 |
| 2017-05-04 | 2 | 2 | 13 |
| 2017-05-04 | 3 | 1 | 4 |
| 2017-05-05 | 4 | 3 | 10 |
| 2017-04-26 | 1 | 1 | 7 |
+------------+-----------+-------------+-----------+
barang_keluar
+------------+-----------+------------+
| tgl_keluar | kd_barang | jml_keluar |
+------------+-----------+------------+
| 2017-05-11 | 1 | 13 |
| 2017-05-12 | 1 | 4 |
| 2017-05-13 | 2 | 5 |
| 2017-05-14 | 3 | 6 |
| 2017-05-15 | 4 | 7 |
| 2017-04-27 | 1 | 5 |
+------------+-----------+------------+

Selanjutnya kita ingin menampilkan data kd_barang, nama_barang,


dan jumlah barang masuk dan keluar untuk bulan Mei 2017 dengan
output sebagai berikut:
+-----------+-------------+-----------+------------+
| kd_barang | nama_barang | jml_masuk | jml_keluar |
+-----------+-------------+-----------+------------+
| 1 | Mouse | 19 | 17 |
| 2 | Flashdisk | 13 | 5 |
| 3 | Mousepad | 4 | 6 |
| 4 | Keyboard | 10 | 7 |
| 5 | Kabel VGA | NULL | NULL |
+-----------+-------------+-----------+------------+

Killer Trik Query MySQL 163


Bagaimana querynya?

Untuk menyusun querynya, kita gunakan prinsip yang telah kita


pelajari yaitu:

Pertama…
Kita identifikasi data yang ingin ditampilkan, yaitu kd_barang,
nama_barang, dan jml_masuk, dan jml_keluar. Ketiganya ada di tabel
barang, barang_masuk, dan barang_keluar, untuk itu kita perlu
menggabungkan ketiga tabel.

Bagaimana bentuk penggabungannya? Kali ini kita gabungkan ketiga


tabel tersebut menggunakan LEFT JOIN dan kita letakkan tabel
barang di paling kiri. Mari kita tes bentuk tabel hasil penggabungan
dengan menggunakan klausa SELECT *:

1. SELECT *
2. FROM barang
3. LEFT JOIN barang_masuk USING(kd_barang)
4. LEFT JOIN barang_keluar USING(kd_barang)

Hasil yang kita peroleh:


+-----------+-------------+------+-------+------------+-------------+-----------+
| kd_barang | nama_barang | stok | harga | tgl_masuk | kd_supplier | jml_masuk |
+-----------+-------------+------+-------+------------+-------------+-----------+
| 1 | Mouse | 14 | 76000 | 2017-05-02 | 1 | 14 |
| 1 | Mouse | 14 | 76000 | 2017-05-03 | 2 | 5 |
| 1 | Mouse | 14 | 76000 | 2017-04-26 | 1 | 7 |
| 1 | Mouse | 14 | 76000 | 2017-05-02 | 1 | 14 |
| 1 | Mouse | 14 | 76000 | 2017-05-03 | 2 | 5 |
| 1 | Mouse | 14 | 76000 | 2017-04-26 | 1 | 7 |
| 2 | Flashdisk | 15 | 55000 | 2017-05-04 | 2 | 13 |
| 3 | Mousepad | 17 | 35000 | 2017-05-04 | 1 | 4 |
| 4 | Keyboard | 12 | 80000 | 2017-05-05 | 3 | 10 |
| 1 | Mouse | 14 | 76000 | 2017-05-02 | 1 | 14 |
| 1 | Mouse | 14 | 76000 | 2017-05-03 | 2 | 5 |
| 1 | Mouse | 14 | 76000 | 2017-04-26 | 1 | 7 |
| 5 | Kabel VGA | 7 | 45000 | NULL | NULL | NULL |
+-----------+-------------+------+-------+------------+-------------+-----------+
Lanjutan...
+------------+------------+

164 9.1. Subquery Sebagai Kolom


| tgl_keluar | jml_keluar |
+------------+------------+
| 2017-05-11 | 13 |
| 2017-05-11 | 13 |
| 2017-05-11 | 13 |
| 2017-05-12 | 4 |
| 2017-05-12 | 4 |
| 2017-05-12 | 4 |
| 2017-05-13 | 5 |
| 2017-05-14 | 6 |
| 2017-05-15 | 7 |
| 2017-04-27 | 5 |
| 2017-04-27 | 5 |
| 2017-04-27 | 5 |
| NULL | NULL |
+------------+------------+

Ternyata tabel yang dihasilkan terdiri dari banyak sekali row, hal ini
seperti yang telah kita bahas pada bagian JOIN, bahwa ketika kita
menggabungkan tabel, maka semua kombinasi dari data yang
berhubungan akan ditampilkan semua.

Kedua…
Setelah memiliki gambaran tabel hasil penggabungan, maka
selanjutnya kita susun kolom pada klausa SELECT, selain itu kita
tambahkan klausa WHERE untuk memfilter data sehingga hanya
diambil data bulan Mei 2017. Oiya jangan lupa menambahkan klausa
GROUP BY karena kita akan menjumlahkan data per barang.

Bentuk query jadinya adalah sebagai berikut:

1. SELECT kd_barang
2. , nama_barang
3. , SUM(jml_masuk) AS jml_masuk
4. , SUM(jml_keluar) AS jml_keluar
5. FROM barang
6. LEFT JOIN barang_masuk USING (kd_barang)
7. LEFT JOIN barang_keluar USING (kd_barang)
8. WHERE (YEAR(tgl_masuk) = 2017 AND YEAR(tgl_masuk) = 2017
9. AND MONTH(tgl_masuk) = 5 AND MONTH(tgl_masuk) = 5)
10. OR tgl_masuk IS NULL OR tgl_keluar IS NULL
11. GROUP BY kd_barang

Killer Trik Query MySQL 165


Hasil yang kita peroleh:
+-----------+-------------+-----------+------------+
| kd_barang | nama_barang | jml_masuk | jml_keluar |
+-----------+-------------+-----------+------------+
| 1 | Mouse | 57 | 44 |
| 2 | Flashdisk | 13 | 5 |
| 3 | Mousepad | 4 | 6 |
| 4 | Keyboard | 10 | 7 |
| 5 | Kabel VGA | NULL | NULL |
+-----------+-------------+-----------+------------+

Ternyata hasil yang kita peroleh berbeda. Kenapa seperti itu? Coba
perhatikan tabel hasil penggabungan, data baik pada kolom
jml_masuk maupun kolom jml_keluar, datanya dobel sehingga ketika
dijumlahkan menggunakan SUM, jumlahnya menjadi lebih besar.

Bagaimana mengatasinya?

Nah, inilah saatnya kita menggunakan subquery…

Tapi… bagaimana mendesain subquerynya?

Untuk menjawab pertanyaan tersebut, selalu gunakan prinsip


hubungan one to one pada JOIN, dimana setiap data pada tabel
hanya berhubungan dengan satu data pada tabel lain, pada kasus
diatas, buat setiap tabel hanya memiliki data kd_barang yang unik,
tidak dobel

Perhatikan gambar dibawah ini:

Gambar 9.2. Ilustrasi Hubungan One to One Pada Tabel barang,


barang_masuk, dan barang_keluar

166 9.1. Subquery Sebagai Kolom


Bagaimana bisa mendapatkan ide bentuk tabel barang_masuk dan
barang_keluar seperti diatas? bentuk diatas didapatkan dari hasil
identifikasi dari permasalahan yang ada, yang pada kebanyakan
kasus sering melibatkan fungsi agregasi (SUM atau COUNT).

TIPS: Subquery sering digunakan ketika hubungan antara


tabel yang digabungkan berbentuk many to many, yang
artinya data pada tabel sebelah kiri berhubungan lebih dari
satu data pada tabel disebelah kanan dan sebaliknya, yang
pada contoh diatas tabel barang_masuk dan barang_keluar

Selanjutnya kita kembali lagi ke tahapan awal

Pertama…
Pertama kita susun ulang hubungan antar tabel, kita terjemahkan
gambar diatas menjadi bentuk query, hasilnya adalah sebagai
berikut:

1. SELECT *
2. FROM barang
3. LEFT JOIN
4. ( SELECT kd_barang, SUM(jml_masuk) AS jml_masuk
5. FROM barang_masuk
6. WHERE MONTH(tgl_masuk) = 5 AND YEAR(tgl_masuk) = 2017
7. GROUP BY kd_barang
8. ) AS barang_masuk USING(kd_barang)
9. LEFT JOIN
10. (
11. SELECT kd_barang, SUM(jml_keluar) AS jml_keluar
12. FROM barang_keluar
13. WHERE MONTH(tgl_keluar) = 5 AND YEAR(tgl_keluar) = 2017
14. GROUP BY kd_barang
15. ) AS brg_keluar USING(kd_barang)

Perhatikan bahwa pada query diatas, kita menggunakan dua


subquery yaitu pada baris 4 – 7 yang menghasilkan tabel
barang_masuk dan 11 s.d 14 untuk barang_keluar. Pada masing

Killer Trik Query MySQL 167


masing subquery kita gunakan klausa WHERE untuk mengambil data
bulan Mei 2017

Perhatikan ilustrasi berikut:

Gambar 9.3. Ilustrasi Eksekusi Query Pada Subquery

Hasil yang kita peroleh adalah:


+-----------+-------------+------+-------+-----------+------------+
| kd_barang | nama_barang | stok | harga | jml_masuk | jml_keluar |
+-----------+-------------+------+-------+-----------+------------+
| 1 | Mouse | 14 | 76000 | 19 | 17 |
| 2 | Flashdisk | 15 | 55000 | 13 | 5 |
| 3 | Mousepad | 17 | 35000 | 4 | 6 |
| 4 | Keyboard | 12 | 80000 | 10 | 7 |
| 5 | Kabel VGA | 7 | 45000 | NULL | NULL |
+-----------+-------------+------+-------+-----------+------------+

Hasil diatas sudah mendekati apa yang kita harapkan, tinggal kita
pilih kolom yang ingin ditampilkan melalui klausa SELECT

168 9.1. Subquery Sebagai Kolom


Kedua….
Seperti tradisi kita, setelah melalui tahap pertama, kita lanjut tahap
kedua yaitu menentukan kolom pada klausa SELECT.

Karena data pada tabel hasil penggabungan sudah sesuai dengan


yang kita harapkan, maka kita tingggal memilih kolom yang ingin kita
tampilkan, tanpa perlu menggunakan fungsi agregasi.

Query lengkapnya adalalah sebagai berikut:

1. SELECT kd_barang, nama_barang, jml_masuk, jml_keluar


2. FROM barang
3. LEFT JOIN
4. ( SELECT kd_barang, SUM(jml_masuk) AS jml_masuk
5. FROM barang_masuk
6. WHERE MONTH(tgl_masuk) = 5 AND YEAR(tgl_masuk) = 2017
7. GROUP BY kd_barang
8. ) AS brg_masuk USING(kd_barang)
9. LEFT JOIN
10. (
11. SELECT kd_barang, SUM(jml_keluar) AS jml_keluar
12. FROM barang_keluar
13. WHERE MONTH(tgl_keluar) = 5 AND YEAR(tgl_keluar) = 2017
14. GROUP BY kd_barang
15. ) AS brg_keluar USING(kd_barang)

Jika query diatas dieksekusi, hasil yang kita peroleh adalah:


+-----------+-------------+-----------+------------+
| kd_barang | nama_barang | jml_masuk | jml_keluar |
+-----------+-------------+-----------+------------+
| 1 | Mouse | 19 | 17 |
| 2 | Flashdisk | 13 | 5 |
| 3 | Mousepad | 4 | 6 |
| 4 | Keyboard | 10 | 7 |
| 5 | Kabel VGA | NULL | NULL |
+-----------+-------------+-----------+------------+

Pada contoh diatas, terdapat klausa AS brg_masuk dan AS


brg_keluar. Klausa ini digunakan untuk memberi nama alias

Killer Trik Query MySQL 169


untuk tabel hasil subquery dan wajib ada. Anda bebas
memberi nama alias sesuai keinginan.

Mungkin anda bertanya tanya, dimana letak tabel hasil subquery ini?
Tabel hasil subquery disimpan di dalam memory (RAM) dan akan
dihapus setelah eksekusi query selesai, oleh karena itu, tabel ini
dinamakan temporary table (tabel sementara). Karena disimpan
pada memory maka semakin besar tabel hasil subquery ini, maka
semakin besar space memory yang digunakan.

170 9.1. Subquery Sebagai Kolom


BAB 10 Menguasai UNION
Seperti yang telah kita bahas pada BAB Menguasai JOIN, pada
MySQL kita dapat menggabungkan dua atau lebih tabel secara
vertikal maupun horizontal

Penggabungan secara horizontal telah kita bahas pada BAB


Menguasai JOIN, nah pada BAB ini kita akan membahas bentuk yang
kedua, yaitu penggabungan vertikal, atas dan bawah.

Ilustrasi penggabungan secara vertikal tampak seperti gambar


dibawah:

Gambar 10.1 Ilustrasi UNION

Killer Trik Query MySQL 171


10.1. Memahami UNION dan UNION ALL
MySQL menyediakan dua buah operator untuk melakukan
penggabungan tabel secara vertikal yaitu UNION dan UNION ALL,
keduanya merupakan operator penting dalam poses pengolahan
data, untuk itu kita perlu memahaminya dengan baik

Format penulisan kedua operator tersebut adalah sebagai berikut:

1. SELECT kolom1, kolom2, kolom3


2. FROM tabel1
3. WHERE kondisi
4. GROUP BY kolom
5. UNION atau UNION ALL
6. SELECT kolom1, kolom2, kolom3
7. FROM tabel2
8. WHERE kondisi
9. GROUP BY kolom
10. [statemen SELECT yang lain…]
11. ORDER BY nama_kolom

Penting diperhatikan bahwa ketika menggunakan operator ini,


statemen SELECT hanya bisa sampai pada klausa GROUP BY
sedangkan operator setelah GROUP BY, yaitu ORDER BY, HAVING, dan
LIMIT hanya bisa digunakan di paling bawah sehingga akan
berpengaruh pada tabel hasil penggabungan, bukan pada masing-
masing statemen SELECT.

Kenapa sampai GROUP BY?

Karena statemen SELECT minimal membutuhkan klausa GROUP BY


khususnya jika statemen SELECT tersebut mengandung fungsi
agregasi, lihat kembali pembahasan mengenai Kunci Memahami
Statemen SELECT

172 BAB 10 Menguasai UNION


Penggunaan UNION dan UNION ALL harus memenuhi kriteria yaitu
jumlah kolom pada masing masing klausa SELECT yang akan
digabungkan harus sama persis.

Selanjutnya, nama kolom tabel hasil penggabungan akan mengikuti


nama kolom pada klausa SELECT yang pertama

Perbedaan UNION dan UNION ALL


UNION dan UNION ALL hanya memiliki satu perbedaan mendasar yaitu
pada baris hasil penggabungan.

UNION akan menggabungkan baris yang memiliki data yang sama di


semua kolomnya, sedangkan UNION ALL akan menggabungkan baris
apa adanya tanpa menggabungkan baris yang memiliki data sama.

Untuk lebih memahami perbedaan UNION dan UNION ALL, misal kita
memiliki tabel kredit_agen dan kredit_survey, dan kredit_closing
sebagai berikut:
kredit_agen
+----+--------------+------------+---------+
| id | nama_pegawai | tanggal | nasabah |
+----+--------------+------------+---------+
| 1 | Alfa | 2017-06-10 | Toni |
| 2 | Beta | 2017-06-11 | Sapta |
| 3 | Charlie | 2017-06-12 | Umar |
| 4 | Beta | 2017-06-13 | Versa |
+----+--------------+------------+---------+
kredit_survey
+----+--------------+------------+---------+
| id | nama_pegawai | tanggal | nasabah |
+----+--------------+------------+---------+
| 1 | Beta | 2017-06-17 | Toni |
| 2 | Charlie | 2017-06-18 | Sapta |
| 3 | Ekky | 2017-06-19 | Umar |
| 4 | Charlie | 2017-06-20 | Versa |
+----+--------------+------------+---------+
kredit_closing
+----+--------------+------------+---------+
| id | nama_pegawai | tanggal | nasabah |

Killer Trik Query MySQL 173


+----+--------------+------------+---------+
| 1 | Charlie | 2017-06-17 | Toni |
| 2 | Ferry | 2017-06-18 | Sapta |
| 3 | Alfa | 2017-06-19 | Umar |
| 4 | Gery | 2017-06-20 | Versa |
+----+--------------+------------+---------+

Selanjutnya kita akan menampilkan semua data pegawai yang


pernah melakukan kegiatan baik calling, survey, maupun closing,
untuk keperluan tersebut, kita gunakan klausa UNION, jalankan
query berikut:

1. SELECT nama_pegawai
2. FROM kredit_agen
3. UNION
4. SELECT nama_pegawai
5. FROM kredit_survey
6. UNION
7. SELECT nama_pegawai
8. FROM kredit_closing

Hasil yang kita peroleh:


+--------------+
| nama_pegawai |
+--------------+
| Alfa |
| Beta |
| Charlie |
| Ekky |
| Ferry |
| Gery |
+--------------+

Dari hasil diatas terlihat tidak ada nama yang muncul lebih dari
sekali, jika kita gunakan UNION ALL, maka nama Charlie akan
muncul empat kali

Selanjutnya kita gabungkan semua data pada ketiga tabel dan kita
beri tanda, mana yang calling, survey, dan closing, jalankan query
berikut:

174 BAB 10 Menguasai UNION


1. SELECT nama_pegawai, "calling" AS jenis
2. FROM kredit_calling
3. UNION ALL
4. SELECT nama_pegawai, "survey" AS jenis
5. FROM kredit_survey
6. UNION ALL
7. SELECT nama_pegawai, "closing" AS jenis
8. FROM kredit_closing

Hasil:
+--------------+---------+
| nama_pegawai | jenis |
+--------------+---------+
| Alfa | calling |
| Beta | calling |
| Charlie | calling |
| Beta | calling |
| Alfa | calling |
| Beta | survey |
| Charlie | survey |
| Ekky | survey |
| Charlie | survey |
| Charlie | closing |
| Ferry | closing |
| Alfa | closing |
| Ferry | closing |
+--------------+---------+

Pada contoh diatas, semua data ditampilkan, jika kita tidak


menggunakan ALL, maka pegawai dengan nama Alfa dan dengan
Jenis calling akan digabung.

Latihan…
Sebagai latihan, coba Anda tampilkan data pegawai beserta jumlah
calling, survey, dan closing nya, tabel output yang kita inginkan
seperti berikut ini:
+--------------+---------+--------+---------+
| nama_pegawai | calling | survey | closing |
+--------------+---------+--------+---------+
| Alfa | 2 | 0 | 1 |

Killer Trik Query MySQL 175


| Beta | 2 | 1 | 0 |
| Charlie | 1 | 2 | 1 |
| Ekky | 0 | 1 | 0 |
| Ferry | 0 | 0 | 2 |
+--------------+---------+--------+---------+

Silakan dicoba…., gunakan prinsip prinsip yang telah kita pelajari


salah satunya slogan "menghitung? ya gunakan COUNT", masih ingat
kan prinsip tersebut?, selain itu gunakan juga prinsip alur eksekusi
query…

Nah silakan di coba lagi….

Sudah bisa…?

Baiklah, mari kita cocokkan jawabannya….

Pertama…
Kita identifikasi bentuk tabel hasil klausa FROM, namun sebelumnya,
karena melibatkan lebih dari satu tabel, kita analisa terlebih dahulu
tabel outputnya.

Dari hasil analisa tabel output, kita membutuhkan data ketiga tabel,
sehingga kita perlu menggabungkan ketiga tabel tersebut menjadi
satu.

Bagaimana bentuk penggabungan nya? Menggunakan JOIN atau


UNION?

Sebelum menjawab pertanyaan tersebut, mari kita breakdown tabel


outputnya. Seperti pada pembahasan sebelumnya tentang COUNT,
mari kita tarik mundur bentuk tabel output, tabel yang kita peroleh
adalah sebagai berikut:
+--------------+---------+--------+---------+
| nama_pegawai | calling | survey | closing |
+--------------+---------+--------+---------+

176 BAB 10 Menguasai UNION


| Alfa | 1 | NULL | NULL |
| Beta | 1 | NULL | NULL |
| Charlie | 1 | NULL | NULL |
| Beta | 1 | NULL | NULL |
| Alfa | 1 | NULL | NULL |
| Beta | NULL | 1 | NULL |
| Charlie | NULL | 1 | NULL |
| Ekky | NULL | 1 | NULL |
| Charlie | NULL | 1 | NULL |
| Charlie | NULL | NULL | 1 |
| Ferry | NULL | NULL | 1 |
| Alfa | NULL | NULL | 1 |
| Ferry | NULL | NULL | 1 |
+--------------+---------+--------+---------+

Sebagai penjelasan, perhatikan ilustrasi berikut:

Gambar 10.2 Ilustrasi Brekdown Tabel Output

Killer Trik Query MySQL 177


Seperti yang telah kita bahas sebelum sebelumnya, selalu
buat tabel antara seperti gambar diatas jika bentuk tabel
output berupa penghitungan (COUNT) atau penjumlahan
(SUM) dan kolom pada tabel output tidak ada pada tabel
hasil klausa FROM

Kembali ke bentuk tabel hasil klausa FROM, untuk mendapatkan


bentuk tabel seperti diatas, maka kita gabungkan ketiga tabel
menggunakan UNION. Mari kita tes hasil penggabungan nya
menggunakan statemen SELECT:

1. SELECT *
2. FROM kredit_calling
3. UNION ALL
4. SELECT *
5. FROM kredit_survey
6. UNION ALL
7. SELECT *
8. FROM kredit_closing

Hasil:
+----+--------------+------------+---------+
| id | nama_pegawai | tanggal | nasabah |
+----+--------------+------------+---------+
| 1 | Alfa | 2017-06-10 | Toni |
| 2 | Beta | 2017-06-11 | Sapta |
| 3 | Charlie | 2017-06-12 | Umar |
| 4 | Beta | 2017-06-13 | Versa |
| 5 | Alfa | 2017-06-14 | Zeti |
| 1 | Beta | 2017-06-17 | Toni |
| 2 | Charlie | 2017-06-18 | Sapta |
| 3 | Ekky | 2017-06-19 | Umar |
| 4 | Charlie | 2017-06-20 | Versa |
| 1 | Charlie | 2017-06-17 | Toni |
| 2 | Ferry | 2017-06-18 | Sapta |
| 3 | Alfa | 2017-06-19 | Umar |
| 4 | Ferry | 2017-06-20 | Versa |
+----+--------------+------------+---------+

178 BAB 10 Menguasai UNION


Tabel tersebut sudah mirip dengan tabel antara seperti pada
gambar, tinggal kita tambahkan kolom calling, survey, dan closing
menggunakan fungsi IF

Tapi tunggu…

Bisakah kita membuatnya? Ternyata tidak bisa.. kenapa? Karena


tidak ada data unik yang dapat membedakan data ketiga tabel, coba
gunakan seperti ini IF(nama_pegawai="...", 1, NULL) AS calling

Bisakah Anda mengisikan titik titk nya? atau mengganti ekspresi


pertama dengan ekspresi lain sehingga menghasilkan data akurat?

Ternyata tidak bisa, untuk itu, kita perlu membuat data baru
sehingga dapat dibedakan mana tabel calling, tabel survey, dan tabel
closing, salah satunya adalah sebagai berikut:
+--------------+---------+
| nama_pegawai | jenis |
+--------------+---------+
| Alfa | calling |
| Beta | calling |
| Charlie | calling |
| Beta | calling |
| Alfa | calling |
| Beta | survey |
| Charlie | survey |
| Ekky | survey |
| Charlie | survey |
| Charlie | closing |
| Ferry | closing |
| Alfa | closing |
| Ferry | closing |
+--------------+---------+

Kolom jenis pada tabel diatas digunakan untuk pembeda tabel, Anda
bebas menggantinya dengan bentuk lain.

Query untuk membuat tabel diatas sudah dibahas diatas, yaitu:

Killer Trik Query MySQL 179


1. SELECT nama_pegawai, "calling" AS jenis
2. FROM kredit_calling
3. UNION ALL
4. SELECT nama_pegawai, "survey" AS jenis
5. FROM kredit_survey
6. UNION ALL
7. SELECT nama_pegawai, "closing" AS jenis
8. FROM kredit_closing

Dengan query diatas, bentuk tabel hasil klausa FROM akan seperti
berikut ini:
+--------------+---------+
| nama_pegawai | jenis |
+--------------+---------+
| Alfa | calling |
| Beta | calling |
| Charlie | calling |
| Beta | calling |
| Alfa | calling |
| Beta | survey |
| Charlie | survey |
| Ekky | survey |
| Charlie | survey |
| Charlie | closing |
| Ferry | closing |
| Alfa | closing |
| Ferry | closing |
+--------------+---------+

Kedua…
Seperti biasa, setelah mendapatkan gambaran tabel hasil klausa
FROM, selanjutnya kita susun kolom pada klausa SELECT

Karena kita perlu kolom baru, yaitu kolom calling, survey, dan closing,
maka kita perlu mendefinisikan kolom tersebut pada klausa SELECT
dengan query sebagai berikut:

1. SELECT nama_pegawai
2. , IF(jenis="calling", 1, NULL) AS calling
3. , IF(jenis="survey", 1, NULL) AS survey

180 BAB 10 Menguasai UNION


4. , IF(jenis="closing", 1, NULL) AS closing
5. FROM
6. (
7. SELECT nama_pegawai, "calling" AS jenis
8. FROM kredit_calling
9. UNION ALL
10. SELECT nama_pegawai, "survey" AS jenis
11. FROM kredit_survey
12. UNION ALL
13. SELECT nama_pegawai, "closing" AS jenis
14. FROM kredit_closing
15. ) AS pegawai

Hasil:
+--------------+---------+--------+---------+
| nama_pegawai | calling | survey | closing |
+--------------+---------+--------+---------+
| Alfa | 1 | NULL | NULL |
| Beta | 1 | NULL | NULL |
| Charlie | 1 | NULL | NULL |
| Beta | 1 | NULL | NULL |
| Alfa | 1 | NULL | NULL |
| Beta | NULL | 1 | NULL |
| Charlie | NULL | 1 | NULL |
| Ekky | NULL | 1 | NULL |
| Charlie | NULL | 1 | NULL |
| Charlie | NULL | NULL | 1 |
| Ferry | NULL | NULL | 1 |
| Alfa | NULL | NULL | 1 |
| Ferry | NULL | NULL | 1 |
+--------------+---------+--------+---------+

Killer Trik Query MySQL 181


Untuk penjelasannya, perhatikan gambar berikut:

Gambar 10.3 Ilustrasi Penggunaan Fungsi IF Pada Klausa SELECT

Bingung? Ingat kembali penjelasan tentang fungsi IF dan prinsip


eksekusi klausa SELECT, yaitu bahwa setiap baris pada tabel hasil
klausa FROM akan dieksekusi sesuai ekspresi pada klausa SELECT.

Nah dari sini sudah kebayang kan? Bagaimana query jadinya?

Yup, querynnya adalah sebagai berikut:

1. SELECT nama_pegawai
2. , COUNT(IF(jenis="calling", 1, NULL)) AS calling
3. , COUNT(IF(jenis="survey", 1, NULL)) AS survey
4. , COUNT(IF(jenis="closing", 1, NULL)) AS closing
5. FROM
6. (
7. SELECT nama_pegawai, "calling" AS jenis
8. FROM kredit_calling
9. UNION ALL
10. SELECT nama_pegawai, "survey" AS jenis
11. FROM kredit_survey

182 BAB 10 Menguasai UNION


12. UNION ALL
13. SELECT nama_pegawai, "closing" AS jenis
14. FROM kredit_closing
15. ) AS pegawai
16. GROUP BY nama_pegawai

Hasil:
+--------------+---------+--------+---------+
| nama_pegawai | calling | survey | closing |
+--------------+---------+--------+---------+
| Alfa | 2 | 0 | 1 |
| Beta | 2 | 1 | 0 |
| Charlie | 1 | 2 | 1 |
| Ekky | 0 | 1 | 0 |
| Ferry | 0 | 0 | 2 |
+--------------+---------+--------+---------+

Pada query diatas, kita tambahkan fungsi COUNT untuk menghitung


jumlah baris pada kolom calling, survey, dan closing, selain itu kita
tambahkan klausa GROUP BY nama_pegawai karena kita akan
mengelompokkan data berdasarkan kolom nama pegawai.

Selanjutnya coba Anda buat baris total untuk baris dan kolom
sebagai berikut:
+--------------+---------+--------+---------+-------+
| nama_pegawai | calling | survey | closing | total |
+--------------+---------+--------+---------+-------+
| Alfa | 2 | 0 | 1 | 3 |
| Beta | 2 | 1 | 0 | 3 |
| Charlie | 1 | 2 | 1 | 4 |
| Ekky | 0 | 1 | 0 | 1 |
| Ferry | 0 | 0 | 2 | 2 |
| TOTAL | 5 | 4 | 4 | 13 |
+--------------+---------+--------+---------+-------+

Killer Trik Query MySQL 183


10.2. Studi Kasus #1
Dalam praktik, union sering digunakan untuk membuat baris total
dan subtotal, pada bagian ini kita akan membahas beberapa kasus
terkait dua hal tersebut

Total
Contoh pertama kita akan membuat baris total yang ada di bagian
paling bawah, misal kita memiliki tabel penjualan sebagai berikut:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Selanjutnya, untuk menambahkan baris total, kita gunakan UNION


ALL, query pertama digunakan untuk mengambil data tabel dan
query kedua untuk membuat baris total.

Agar menghasilkan baris total, gunakan fungsi agregasi pada query


kedua, query jadinya adalah sebagai berikut:

1. SELECT id_trx, tgl_trx, total_trx


2. FROM penjualan
3. UNION ALL
4. SELECT 'TOTAL', "", SUM(total_trx)
5. FROM penjualan

Hasil:
+--------+------------+-----------+
| id_trx | tgl_trx | total_trx |
+--------+------------+-----------+
| 1 | 2017-03-02 | 192000 |

184 BAB 10 Menguasai UNION


| 2 | 2017-03-10 | 186000 |
| 3 | 2017-04-10 | 259000 |
| 4 | 2017-04-05 | 110000 |
| 5 | 2016-11-10 | 256000 |
| TOTAL | | 1003000 |
+--------+------------+-----------+

Pada contoh diatas terlihat sebuah baris baru yang berisi total nilai
kolom total_trx

Cara ini jauh lebih mudah dibanding menggunakan klausa WITH


ROLLUP (dibahas pada bab selanjutnya)

Selanjutnya, sebagai latihan coba Anda ubah query diatas sehingga


menampilkan kolom id_pelanggan dan nama_pelanggan sebagai
berikut:
+--------+--------------+------+------------+-----------+
| id_trx | id_pelanggan | nama | tgl_trx | total_trx |
+--------+--------------+------+------------+-----------+
| 1 | 1 | Alfa | 2017-03-02 | 192000 |
| 2 | 1 | Alfa | 2017-03-10 | 186000 |
| 4 | 2 | Beta | 2017-04-05 | 110000 |
| 5 | 2 | Beta | 2016-11-10 | 256000 |
| 3 | 0 | NULL | 2017-04-10 | 259000 |
| TOTAL | | | | 1003000 |
+--------+--------------+------+------------+-----------+

Bagaimana? Sudah bisa kan…? Jangan lupa, selalu gunakan prinsip


urutan eksekusi query…

Baiklah, jika sudah mari kita cocokkan jawabannya

Berikut ini query versi saya:

1. SELECT id_trx, id_pelanggan, nama, tgl_trx, total_trx


2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan)
4. UNION ALL
5. SELECT 'TOTAL', "", "", "", SUM(total_trx)
6. FROM penjualan

Killer Trik Query MySQL 185


Tips membuat total dengan subquery merupakan cara yang
powerful dapat menghasilkan hasil yang konsisten sehingga
cara ini layak untuk digunakan dalam praktek nyata

Sub Total
Selain baris total, dalam praktik kita juga sering membuat baris sub
total, pada subtotal, data dikelompokkan berdasarkan kriteria
tertentu.

Misal melanjutkan contoh sebelumnya, kita buat baris SUB TOTAL


yang berisi total penjualan per pelanggan, hasil yang kita inginkan
adalah sebagai berikut:
+-----------+--------------+-----------+------------+-----------+
| id_trx | id_pelanggan | nama | tgl_trx | total_trx |
+-----------+--------------+-----------+------------+-----------+
| 3 | 0 | NULL | 2017-04-10 | 259000 |
| SUB TOTAL | 0 | SUB TOTAL | | 259000 |
| 1 | 1 | Alfa | 2017-03-02 | 192000 |
| 2 | 1 | Alfa | 2017-03-10 | 186000 |
| SUB TOTAL | 1 | SUB TOTAL | | 378000 |
| 4 | 2 | Beta | 2017-04-05 | 110000 |
| 5 | 2 | Beta | 2016-11-10 | 256000 |
| SUB TOTAL | 2 | SUB TOTAL | | 366000 |
+-----------+--------------+-----------+------------+-----------+

Bagaimana querynya?

Sebagai clue (petunjuk), kali ini kita akan menggunakan dua buah
tabel yaitu tabel penjualan dan pelanggan sebagai berikut:
penjualan
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+
pelanggan

186 BAB 10 Menguasai UNION


+--------------+---------+-----------+---------+
| id_pelanggan | nama | alamat | id_staf |
+--------------+---------+-----------+---------+
| 1 | Alfa | Jakarta | 1 |
| 2 | Beta | Semarang | 1 |
| 3 | Charlie | Surabaya | 2 |
| 4 | Delta | Surakarta | 3 |
+--------------+---------+-----------+---------+

Silakan dicoba….

Sudah bisa?

Baiklah mari kita bahas…

Pertama…
Seperti biasa kita susun klausa FROM, karena melibatkan dua tabel,
kita harus menggunakan JOIN, pilihannya JOIN atau LEFT JOIN.

Karena kita akan menampilkan semua data penjualan, maka kita


gunakan LEFT JOIN dan kita letakkan tabel penjualan di sebelah kiri.

Mari kita tes dengan menjalankan query berikut:

1. SELECT id_trx, id_pelanggan, nama, tgl_trx, total_trx


2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan)

Hasil yang kita peroleh:


+--------+--------------+------+------------+-----------+
| id_trx | id_pelanggan | nama | tgl_trx | total_trx |
+--------+--------------+------+------------+-----------+
| 1 | 1 | Alfa | 2017-03-02 | 192000 |
| 2 | 1 | Alfa | 2017-03-10 | 186000 |
| 4 | 2 | Beta | 2017-04-05 | 110000 |
| 5 | 2 | Beta | 2016-11-10 | 256000 |
| 3 | 0 | NULL | 2017-04-10 | 259000 |
+--------+--------------+------+------------+-----------+

Apakah sudah mirip dengan hasil?

Killer Trik Query MySQL 187


Ya, sudah, tinggal kita tambahkan baris SUB TOTAL.

Tapi… bagaimana menambahkannya?

Pasti Anda akan menjawab: karena topik pembahasannya adalah


UNION pasti caranya menggunakan UNION

Yup betul sekali 

Terus bagaimana caranya?

Caranya, kita gunakan UNION dan ORDER BY. Pertama kita buat total
per id_pelanggan.

Kenapa per id_pelanggan? Ya karena kita akan membuat sub total


per pelanggan. Jalankan query berikut:

1. SELECT id_trx, id_pelanggan, nama, tgl_trx, total_trx


2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan)
4. UNION ALL
5. SELECT 'SUB TOTAL', id_pelanggan, "SUB TOTAL", "", SUM(total_trx)
6. FROM penjualan
7. GROUP BY id_pelanggan

Hasilnya adalah:
+-----------+--------------+-----------+------------+-----------+
| id_trx | id_pelanggan | nama | tgl_trx | total_trx |
+-----------+--------------+-----------+------------+-----------+
| 1 | 1 | Alfa | 2017-03-02 | 192000 |
| 2 | 1 | Alfa | 2017-03-10 | 186000 |
| 4 | 2 | Beta | 2017-04-05 | 110000 |
| 5 | 2 | Beta | 2016-11-10 | 256000 |
| 3 | 0 | NULL | 2017-04-10 | 259000 |
| SUB TOTAL | 0 | SUB TOTAL | | 259000 |
| SUB TOTAL | 1 | SUB TOTAL | | 378000 |
| SUB TOTAL | 2 | SUB TOTAL | | 366000 |
+-----------+--------------+-----------+------------+-----------+

188 BAB 10 Menguasai UNION


Selanjutnya dengan ORDER BY kita ubah urutan sehingga baris SUB
TOTAL berada di bawah tiap tiap nama pelanggan, kita tambahkan
klausa ORDER BY di baris terakhir sebagai berikut:

1. SELECT id_trx, id_pelanggan, nama, tgl_trx, total_trx


2. FROM penjualan
3. LEFT JOIN pelanggan USING(id_pelanggan)
4. UNION ALL
5. SELECT 'SUB TOTAL'
6. , id_pelanggan, "SUB TOTAL", "", SUM(total_trx)
7. FROM penjualan
8. GROUP BY id_pelanggan
9. ORDER BY id_pelanggan, id_trx

Hasil yang kita peroleh:


+-----------+--------------+-----------+------------+-----------+
| id_trx | id_pelanggan | nama | tgl_trx | total_trx |
+-----------+--------------+-----------+------------+-----------+
| 3 | 0 | NULL | 2017-04-10 | 259000 |
| SUB TOTAL | 0 | SUB TOTAL | | 259000 |
| 1 | 1 | Alfa | 2017-03-02 | 192000 |
| 2 | 1 | Alfa | 2017-03-10 | 186000 |
| SUB TOTAL | 1 | SUB TOTAL | | 378000 |
| 4 | 2 | Beta | 2017-04-05 | 110000 |
| 5 | 2 | Beta | 2016-11-10 | 256000 |
| SUB TOTAL | 2 | SUB TOTAL | | 366000 |
+-----------+--------------+-----------+------------+-----------+

Pada contoh diatas, pertama data diurutkan berdasarkan


id_pelanggan sehingga baris sub total akan berada pada tiap-tiap
pelanggan sesuai dengan id_pelanggannya

Selanjutnya data diurutkan berdasarkan kolom id_trx, sehingga baris


SUB TOTAL berada di bawah, kenapa dibawah? karena berupa
abjad, bukan numeric sebagaimana id_trx yang lain

Anda bisa membuat baris subtotal tersebut berada di atas dengan


menambahkan DESC pada id_trx menjadi ORDER BY id_pelanggan,
id_trx DESC

Killer Trik Query MySQL 189


NOTE: untuk lebih memudahkan mengurutkan baris total dan sub
total, kita bisa memberi nilai kolom dengan NULL bukan TOTAL atau
SUB TOTAL. Hal ini lebih memudahkan karena nilai NULL akan selalu
di paling atas jika diurutkan secara ascending dan di paling bawah
jika diurutkan secara descending.

Penerapan teknik ini dapat diikuti pada BAB 11 menguasai WITH


ROLLUP.

10.3. Studi Kasus #2


Studi kasus ini merupakan kelanjutan dari contoh Sub Total yang
telah kita bahas pada Studi Kasus #1

Pada tabel output contoh kasus sebelumnya, terdapat kolom yang


tidak diperlukan, seperti kolom id_trx dan id_pelanggan, untuk itu
kita akan menghilangkannya.

Selain itu kita juga akan menambahkan baris total pada bagian akhir
tabel, sehingga hasil akhir tabel adalah sebagai berikut:
+----------------+-------------------+-----------------+
| Nama Pelanggan | Tanggal Transaksi | Nilai Transaksi |
+----------------+-------------------+-----------------+
| - | 2017-04-10 | 259000 |
| SUB TOTAL | | 259000 |
| Alfa | 2017-03-02 | 192000 |
| Alfa | 2017-03-10 | 186000 |
| SUB TOTAL | | 378000 |
| Beta | 2017-04-05 | 110000 |
| Beta | 2016-11-10 | 256000 |
| SUB TOTAL | | 366000 |
| TOTAL | | 1003000 |
+----------------+-------------------+-----------------+

Jauh lebih rapi dan informatif kan?

Oke, tapi bagaimana caranya?

190 BAB 10 Menguasai UNION


Baiklah, karena teknik ini belum pernah kita bahas, mari kita bahas
step-step nya.

Masih ingat pembahasan subquery? Nah, karena kita akan


menghilangkan beberapa kolom pada tabel hasil query sub total
maka kita tempatkan query sub total menjadi subquery

Remainding: Ingat kembali jika kita ingin mengolah lagi tabel


hasil dari suatu query, maka gunakan subquery.

Jalankan query berikut:

1. SELECT IFNULL(nama, "-") AS "Nama Pelanggan"


2. , tgl_trx AS "Tanggal Transaksi"
3. , total_trx AS "Nilai Transaksi"
4. FROM
5. (
6. SELECT id_trx, id_pelanggan, nama, tgl_trx, total_trx
7. FROM penjualan
8. LEFT JOIN pelanggan USING(id_pelanggan)
9. UNION ALL
10. SELECT "SUB TOTAL", id_pelanggan
11. , "SUB TOTAL", "", SUM(total_trx)
12. FROM penjualan
13. GROUP BY id_pelanggan
14. ORDER BY id_pelanggan, id_trx
15. ) AS penjualan

Hasilnya adalah:
+----------------+-------------------+-----------------+
| Nama Pelanggan | Tanggal Transaksi | Nilai Transaksi |
+----------------+-------------------+-----------------+
| - | 2017-04-10 | 259000 |
| SUB TOTAL | | 259000 |
| Alfa | 2017-03-02 | 192000 |
| Alfa | 2017-03-10 | 186000 |
| SUB TOTAL | | 378000 |
| Beta | 2017-04-05 | 110000 |
| Beta | 2016-11-10 | 256000 |
| SUB TOTAL | | 366000 |
+----------------+-------------------+-----------------+

Killer Trik Query MySQL 191


Mungkin Anda akan bertanya tanya kenapa kita tidak langsung
menghilangkan id_trx, id_pelanggan pada query utama, sehingga
tidak perlu subquery?

Hal ini tidak kita lakukan karena kedua kolom itu kita gunakan untuk
mengurutkan data, lihat klausa ORDER BY

Kembali ke tabel hasil query… hasilnya sudah mirip dengan yang kita
inginkan bukan? Nah selanjutnya tinggal kita tambahkan baris total.

Silakan Anda coba ya?

Bisa?

Baiklah, mari kita cocokkan. Query merurut versi saya adalah sebagai
berikut:

1. SELECT IFNULL(nama, "-") AS "Nama Pelanggan"


2. , tgl_trx AS "Tanggal Transaksi"
3. , total_trx AS "Nilai Transaksi"
4. FROM
5. (
6. SELECT id_trx, id_pelanggan, nama, tgl_trx, total_trx
7. FROM penjualan
8. LEFT JOIN pelanggan USING(id_pelanggan)
9. UNION ALL
10. SELECT "SUB TOTAL", id_pelanggan
11. , "SUB TOTAL", "", SUM(total_trx)
12. FROM penjualan
13. GROUP BY id_pelanggan
14. ORDER BY id_pelanggan, id_trx
15. ) AS penjualan
16. UNION ALL
17. SELECT "TOTAL", "", SUM(total_trx)
18. FROM penjualan

Hasil:
+----------------+-------------------+-----------------+
| Nama Pelanggan | Tanggal Transaksi | Nilai Transaksi |
+----------------+-------------------+-----------------+
| - | 2017-04-10 | 259000 |

192 BAB 10 Menguasai UNION


| SUB TOTAL | | 259000 |
| Alfa | 2017-03-02 | 192000 |
| Alfa | 2017-03-10 | 186000 |
| SUB TOTAL | | 378000 |
| Beta | 2017-04-05 | 110000 |
| Beta | 2016-11-10 | 256000 |
| SUB TOTAL | | 366000 |
| TOTAL | | 1003000 |
+----------------+-------------------+-----------------+

Yes, sudah berhasil.

Sudah sama dengan yang Anda buat?

Equivalen / Alternatif
Seribu jalan menuju Roma, banyak cara memecahkan masalah.
Demikian juga dengan query total dan sub total diatas, kita dapat
menggunakan cara lain untuk menghasilkan tabel yang sama persis.

Pertanyaannya bagaimana caranya?

Caranya.. menggunakan WITH ROLLUP, yang akan dibahas pada BAB


selanjutnya

Namun jika Anda penasaran ingin segera tahu querynya seperti apa,
berikut ini query jadinya:

1. SELECT CASE
2. WHEN id_trx IS NULL AND id_pelanggan IS NOT NULL
3. THEN "SUB TOTAL"
4. WHEN id_trx IS NULL AND id_pelanggan IS NULL
5. THEN "TOTAL"
6. WHEN nama IS NULL
7. THEN "-"
8. ELSE nama
9. END AS Nama
10. , IF(ISNULL(id_trx), "", tgl_trx) AS "Tanggal Transaksi"
11. , total_trx AS "Total Transaksi"
12. FROM
13. (
14. SELECT id_trx, id_pelanggan

Killer Trik Query MySQL 193


15. , nama, tgl_trx, SUM(total_trx) AS total_trx
16. FROM penjualan
17. LEFT JOIN pelanggan USING(id_pelanggan)
18. GROUP BY id_pelanggan, id_trx
19. WITH ROLLUP
20. ) AS penjualan

Hasilnya:
+----------------+-------------------+-----------------+
| Nama Pelanggan | Tanggal Transaksi | Nilai Transaksi |
+----------------+-------------------+-----------------+
| - | 2017-04-10 | 259000 |
| SUB TOTAL | | 259000 |
| Alfa | 2017-03-02 | 192000 |
| Alfa | 2017-03-10 | 186000 |
| SUB TOTAL | | 378000 |
| Beta | 2017-04-05 | 110000 |
| Beta | 2016-11-10 | 256000 |
| SUB TOTAL | | 366000 |
| TOTAL | | 1003000 |
+----------------+-------------------+-----------------+

Menurut Anda, mana yang lebih mudah dan efisien?

Saya pribadi tidak begitu confident menggunakan WITH ROLLUP,


karena kita tidak memiliki kontrol penuh terhadap hasil query,
kenapa? karena pada WITH ROLLUP kita sangat bergantung pada
klausa GROUP BY

10.4. Studi Kasus #3


Pada pembahasan sebelumnya tentang SUB TOTAL, kita telah
belajar bagaimana menggunakan klausa ORDER BY pada UNION,
pada contoh tersebut, ORDER BY di tulis pada bagian paling bawah
sehingga berpengaruh pada tabel hasil penggabungan.

194 BAB 10 Menguasai UNION


Nah, pada kondisi tertentu, kita ingin menggunakan klausa ORDER
BY ini pada masing-masing query SELECT, tidak pada keseluruhan
hasil query.

Nah, studi kasus kali ini kita akan membahas bagaimana cara
melakukan itu, kita gunakan contoh pada pembahasan pembuatan
baris total, mari kita lihat kembali querynya:

1. SELECT id_trx, tgl_trx, total_trx


2. FROM penjualan
3. UNION ALL
4. SELECT 'TOTAL', "", SUM(total_trx)
5. FROM penjualan

Hasil:
+--------+------------+-----------+
| id_trx | tgl_trx | total_trx |
+--------+------------+-----------+
| 1 | 2017-03-02 | 192000 |
| 2 | 2017-03-10 | 186000 |
| 3 | 2017-04-10 | 259000 |
| 4 | 2017-04-05 | 110000 |
| 5 | 2016-11-10 | 256000 |
| TOTAL | | 1003000 |
+--------+------------+-----------+

Selanjutnya kita akan urutkan data transaksi mulai dari yang


terbesar, sehingga dapat mudah diketahui pelanggan mana yang
memberikan kontribusi terbesar.

Hasil yang diinginkan tampak seperti tabel berikut:


+-------+------------+-----------+
| nama | tgl_trx | total_trx |
+-------+------------+-----------+
| - | 2017-04-10 | 259000 |
| Beta | 2016-11-10 | 256000 |
| Alfa | 2017-03-02 | 192000 |
| Alfa | 2017-03-10 | 186000 |
| Beta | 2017-04-05 | 110000 |
| TOTAL | | 1003000 |
+-------+------------+-----------+

Killer Trik Query MySQL 195


Bagaimana Query nya?

Jika Anda paham pembahasan pada teknik pembuatan SUB TOTAL


maka akan mudah menyelesaikan case ini.

Query lengkapnya adalah sebagai berikut:

1. SELECT *
2. FROM
3. ( SELECT IFNULL(nama, "-") AS nama, tgl_trx, total_trx
4. FROM penjualan
5. LEFT JOIN pelanggan USING (id_pelanggan)
6. ORDER BY total_trx DESC
7. ) AS penjualan_rinci
8. UNION ALL
9. SELECT 'TOTAL',"", SUM(total_trx)
10. FROM penjualan

Mungkin Anda bertanya tanya kenapa tidak langsung menggunakan


ORDER BY di query utama kemudian diberi tanda kurung seperti ini?

1. ( SELECT IFNULL(nama, "-") AS nama, tgl_trx, total_trx


2. FROM penjualan
3. LEFT JOIN pelanggan USING (id_pelanggan)
4. ORDER BY total_trx DESC
5. )
6. UNION ALL
7. ( SELECT 'TOTAL',"", SUM(total_trx)
8. FROM penjualan)

Query tersebut dapat berjalan dengan baik, namun data pada query
pertama tidak berubah, kenapa? Karena seperti pada konsep
UNION, MySQL tidak memproses klausa setelah GROUP BY yang ada
pada pada masing masing klausa SELECT yang dilakukan secara
langsung (tanpa subquery).

Dengan demikian kita harus menggunakan subquery.

196 BAB 10 Menguasai UNION


BAB 11 Menguasai WITH ROLLUP
Pada bab UNION kita telah belajar bagaimana membuat baris total
dan subtotal, pada MySQL terdapat fitur khusus untuk menangani
hal tersebut yaitu dengan klausa WITH ROLLUP, apakah klausa ini
lebih powerful dibanding UNION? Kita bahas tuntas di bab ini.

11.1. Memahami Cara Kerja WITH ROLLUP


WITH ROLLUP bekerja dengan menjumlahkan nilai kolom yang
terdapat fungsi agregasinya, sehingga jika ingin kolom terebut
muncul baris totalnya, maka kolom tersebut harus diberi fungsi
agregasi

Selanjutnya, selain fungsi agregasi, untuk dapat menggunakan


klausa WITH ROLLUP, kita harus menggunakan klausa GROUP BY,
klausa ini harus diletakkan tepat sebelum klausa WITH ROLLUP.

Selain itu, ketika menggunakan klausa ini, kita tidak dapat


menggunakan klausa ORDER BY, namun dapat mneggunakan klausa
lain seperti HAVING dan LIMIT

Untuk lebih jelasnya, misal kita miliki tabel penjualan dengan data
sebagai berikut:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Killer Trik Query MySQL 197


Selanjutnya kita tampilkan data penjualan tersebut dengan
tambahan baris total di bagian akir baris, jalankan query berikut:

1. SELECT id_trx, id_pelanggan, tgl_trx, SUM(total_trx)


2. FROM penjualan
3. GROUP BY id_trx
4. WITH ROLLUP

Hasil:
+--------+--------------+------------+----------------+
| id_trx | id_pelanggan | tgl_trx | SUM(total_trx) |
+--------+--------------+------------+----------------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
| NULL | 2 | 2016-11-10 | 1003000 |
+--------+--------------+------------+----------------+

Perhatikan bahwa pada query diatas, dengan GROUP BY kita


kelompokkan data berdasar kolom id_trx meskipun riilnya tidak ada
data yang dikelompokkan, karena tidak ada id_trx yang sama, hal ini
hanya untuk "mengakali" saja agar kita dapat menggunakan WITH
ROLLUP

Agak maksa? Ya memang harus seperti itu…..

Selain itu perhatikan bahwa kita juga menggunakan fungsi agregasi


SUM pada kolom total_trx, sehingga pada baris total kita dapatkan
jumlah total semua transaksi.

Ciri khas baris hasil WITH ROLLUP ini adalah adanya nilai
NULL pada kolom yang ada pada klausa GROUP BY (pada
contoh diatas kolom id_trx pada baris total), sedangkan nilai
pada kolom lain (yang tidak ada fungsi agregasinya) biasanya
sama dengan nilai pada baris sebelumnya

198 BAB 11 Menguasai WITH ROLLUP


Selanjutnya mari kita lanjutkan pembahasan dengan membuat baris
subtotal, misal kita kelompokkan data berdasarkan bulan transaksi
dan id_pelanggan serta kita tampilkan data nama pelanggan.

Adapun data tabel pelanggan adalah sebagai berikut:


+--------------+---------+-----------+---------+
| id_pelanggan | nama | alamat | id_staf |
+--------------+---------+-----------+---------+
| 1 | Alfa | Jakarta | 1 |
| 2 | Beta | Semarang | 1 |
| 3 | Charlie | Surabaya | 2 |
| 4 | Delta | Surakarta | 3 |
+--------------+---------+-----------+---------+

Selanjutnya jalankan query berikut:

1. SELECT MONTH(tgl_trx) AS bln_trx


2. , id_pelanggan
3. , nama
4. , COUNT(id_trx) AS jml_trx
5. , SUM(total_trx) AS nilai_trx
6. FROM penjualan
7. LEFT JOIN pelanggan USING (id_pelanggan)
8. GROUP BY bln_trx, id_pelanggan
9. WITH ROLLUP

Hasil yang kita peroleh adalah:


+---------+--------------+------+---------+-----------+
| bln_trx | id_pelanggan | nama | jml_trx | nilai_trx |
+---------+--------------+------+---------+-----------+
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | NULL | Alfa | 2 | 378000 |
| 4 | 0 | NULL | 1 | 259000 |
| 4 | 2 | Beta | 1 | 110000 |
| 4 | NULL | Beta | 2 | 369000 |
| 11 | 2 | Beta | 1 | 256000 |
| 11 | NULL | Beta | 1 | 256000 |
| NULL | NULL | Beta | 5 | 1003000 |
+---------+--------------+------+---------+-----------+

Killer Trik Query MySQL 199


Perhatikan bahwa pada contoh diatas, terdapat beberapa baris
tambahan yang dihasilkan dari klausa WITH ROLLUP (baris yang
mengandung nilai NULL), yaitu pada kolom bln_trx dan id_pelanggan.

Perhatikan juga bahwa WITH ROLLUP mengkalkulasi nilai pada kolom


yang terdapat fungsi COUNT() dan SUM() yaitu kolom jml_trx dan
nilai_trx

Karena pada klausa GROUP BY kita mengelompokkan data urut


berdasarkan bulan, baru kemudian id_pelanggan, maka ketika
MySQL membuat total untuk kelompok tersebut, MySQL akan
memberikan nilai NULL pada kolom id_pelanggan (untuk subtotal
setiap bulan) dan id_pelanggan + bulan untuk total semua data,
perhatikan gambar berikut:

Gambar 11.1 Ilustrsi WITH ROLLUP Pada Beberapa Kolom

Sampai disini sudah paham kan? Bagaimana klausa WITH ROLLUP


bekerja? Jika sudah, mari kita lanjutkan…

200 BAB 11 Menguasai WITH ROLLUP


11.2. Mengganti Nilai NULL
Pada bagian sebelumnya terlihat bahwa dengan WITH ROLLUP nilai
kolom yang ada pada klausa GROUP BY akan bernilai NULL. Agar
lebih memiliki arti, kita perlu menggantinya dengan kata lain, seperti
TOTAL atau SUB TOTAL

Melanjutkan contoh sebelumnya, kita kelompokkan data penjualan


berdasarkan bulan dan id_pelanggan dan kita ganti nilai NULL
dengan string TOTAL dan SUB TOTAL

Jalankan query berikut:

1. SELECT MONTH(tgl_trx) AS bln_trx


2. , id_pelanggan
3. , nama
4. , COUNT(id_trx) AS jml_trx
5. , SUM(total_trx) AS nilai_trx
6. FROM penjualan
7. LEFT JOIN pelanggan USING (id_pelanggan)
8. GROUP BY bln_trx, id_pelanggan
9. WITH ROLLUP

Hasilnya adalah:
+---------+-----------+------+---------+-----------+
| bln_trx | id | nama | jml_trx | nilai_trx |
+---------+-----------+------+---------+-----------+
| 11 | 2 | Beta | 1 | 256000 |
| 11 | SUB TOTAL | Beta | 1 | 256000 |
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | SUB TOTAL | Alfa | 2 | 378000 |
| 4 | 0 | NULL | 1 | 259000 |
| 4 | 2 | Beta | 1 | 110000 |
| 4 | SUB TOTAL | Beta | 2 | 369000 |
| NULL | SUB TOTAL | Beta | 5 | 1003000 |
+---------+-----------+------+---------+-----------+

Pada contoh diatas, kita berhasil mengubah nilai NULL pada kolom
id menjadi SUB TOTAL, namun kenapa nilai pada bln_trx tetap NULL?

Killer Trik Query MySQL 201


Jawabnya, memang untuk fungsi tertentu, khususnya fungsi terkait
date time, kita tidak dapat mengubah NULL secara langsung seketika
saat fungsi dijalankan, sebagai solusinya, kita dapat menggunakan
subquery sebagai berikut:

1. SELECT IFNULL(bln_trx, "TOTAL") AS bulan


2. , id
3. , nama
4. , jml_trx
5. , nilai_trx
6. FROM
7. (SELECT MONTH(tgl_trx) AS bln_trx
8. , IFNULL(id_pelanggan, "TOTAL") AS id, nama
9. , COUNT(id_trx) AS jml_trx
10. , SUM(total_trx) AS nilai_trx
11. FROM penjualan
12. LEFT JOIN pelanggan USING (id_pelanggan)
13. GROUP BY bln_trx, id_pelanggan
14. WITH ROLLUP
15. ) AS penjualan

Hasil:
+-------+-------+------+---------+-----------+
| bulan | id | nama | jml_trx | nilai_trx |
+-------+-------+------+---------+-----------+
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | TOTAL | Alfa | 2 | 378000 |
| 4 | 0 | NULL | 1 | 259000 |
| 4 | 2 | Beta | 1 | 110000 |
| 4 | TOTAL | Beta | 2 | 369000 |
| 11 | 2 | Beta | 1 | 256000 |
| 11 | TOTAL | Beta | 1 | 256000 |
| TOTAL | TOTAL | Beta | 5 | 1003000 |
+-------+-------+------+---------+-----------+

Kenapa subquery? Ingat kembali prinsip bahwa jika kita ingin


mengolah tabel hasil query, maka kita harus menggunakan
subquery, kenapa? karena pada subquery, temporary tabel sudah
terbentuk sehingga tabel tersebut dapat diolah layaknya tabel riil.

202 BAB 11 Menguasai WITH ROLLUP


11.3. ORDER BY Pada WITH ROLLUP
Seperti disampaikan diawal, bahwa ketika menggunakan WITH
ROLLUP kita mutlak tidak dapat menggunakan klausa ORDER BY,
sehingga jika kita ingin mengurutkan data (termasuk baris baru hasil
WITH ROLLUP) kita harus menggunakan subquery.

Misal melanjutkan contoh sebelumnya, kita urutkan data


berdasarkan bulan secara desending, hasil yang kita harapkan
adalah sebagai berikut:
+---------+-----------+------+---------+-----------+
| bln_trx | id | nama | jml_trx | nilai_trx |
+---------+-----------+------+---------+-----------+
| 4 | 0 | NULL | 1 | 259000 |
| 4 | 2 | Beta | 1 | 110000 |
| 4 | SUB TOTAL | Beta | 2 | 369000 |
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | SUB TOTAL | Alfa | 2 | 378000 |
| NULL | SUB TOTAL | Alfa | 4 | 747000 |
+---------+-----------+------+---------+-----------+

Bagaimana querynya?

Untuk memperoleh hasil seperti tabel diatas, kita tinggal tambahkan


DESC pada ORDER BY sebagai berikut:

1. SELECT MONTH(tgl_trx) AS bln_trx


2. , IFNULL(id_pelanggan, "SUB TOTAL") AS id, nama
3. , COUNT(id_trx) AS jml_trx
4. , SUM(total_trx) AS nilai_trx
5. FROM penjualan
6. LEFT JOIN pelanggan USING (id_pelanggan)
7. WHERE YEAR(tgl_trx) = 2017
8. GROUP BY bln_trx DESC, id_pelanggan
9. WITH ROLLUP

Perhatikan bahwa pada baris nomor 8 kita tambahkan DESC setelah


kolom bln_trx, simpel kan?

Killer Trik Query MySQL 203


Kenapa bisa seperti itu?

Seperti yang telah kita bahas pada BAB I, ketika MySQL menjalankan
GROUP BY, maka otomatis MySQL akan mengurutkan data
berdasarkan data kolom yang ada pada klausa GROUP BY, hal ini
disebut implisit order.

Secara default, implisit order berbentuk ascending (dari kecil ke


besar), namun demikian kita dapat mengubahnya menjadi
descending, yaitu dengan menambahkan DESC pada kolom yang
ingin kita ubah urutannya.

Tetapi…
Tetapi…. sejak MySQL 5.7 cara ini sudah deprecated yang artinya
akan dihilangkan pada versi berikutnya (entah kapan), sehingga jika
kita menggunakan teknik ini, maka kode yang kita buat tidak “aman”
yang artinya tidak akan berjalan pada MySQL versi terbaru.

Saya pribadi tidak nyaman jika menggunakan fitur yang deprecated,


karena kedepannya mau tidak mau kita harus beralih ke versi
terbaru, entah karena hardware yang sudah tidak kompatibel,
kebijakan management yang berubah, dll sehingga sebisa mungkin
hindari fitur deprecated.

Ketika menjalankan klausa GROUP BY, dibelakang layar


MySQL akan mengurutkan data kolom yang ada pada klausa
group by secara ascending (implisit order), kita dapat
mengubah pola pengurutan ini dengan menambahkan
DESC pada kolom yang ada pada klausa GROUP BY

Sejak MySQL versi 5.7, fitur ini deprecated, yang artinya akan
dihilangkan pada MySQL versi berikutnya, sehingga

204 BAB 11 Menguasai WITH ROLLUP


disarankan untuk menggunakan eksplisit order dengan
menuliskan klausa ORDER BY

Nah, melanjutkan case sebelumnya, bagaimana query yang kita


gunakan jika menggunakan eksplisit order?

Silakan dicoba, sebagai clue, kita akan gunakan subquery

Sudah bisa?

Baiklah, mari kita cocokkan. Query versi saya adalah sebagai berikut:

1. SELECT *
2. FROM
3. (SELECT MONTH(tgl_trx) AS bln_trx
4. , id_pelanggan AS id, nama
5. , COUNT(id_trx) AS jml_trx
6. , SUM(total_trx) AS nilai_trx
7. FROM penjualan
8. LEFT JOIN pelanggan USING (id_pelanggan)
9. WHERE YEAR(tgl_trx) = 2017
10. GROUP BY bln_trx, id_pelanggan
11. WITH ROLLUP
12. ) AS penjualan
13. ORDER BY bln_trx DESC, id DESC

Hasil yang kita peroleh adalah:


+---------+------+------+---------+-----------+
| bln_trx | id | nama | jml_trx | nilai_trx |
+---------+------+------+---------+-----------+
| 4 | 2 | Beta | 1 | 110000 |
| 4 | 0 | NULL | 1 | 259000 |
| 4 | NULL | Beta | 2 | 369000 |
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | NULL | Alfa | 2 | 378000 |
| NULL | NULL | Beta | 4 | 747000 |
+---------+------+------+---------+-----------+

Penjelasan:

Killer Trik Query MySQL 205


 Karena terdapat WITH ROLLUP maka untuk menggunakan
ORDER BY, mau tidak mau kita harus menggunakan subquery

 Kenapa kembali ke nilai NULL? bukan kata kata TOTAL atau SUB
TOTAL? kita menggunakan nilai NULL agar mudah mengatur
posisi baris null, karena jika kita urutkan secara ascending, nilai
NULL akan selalu diatas sedangkan untuk descending akan
selalu dibawah

Untuk memudahkan penempatan baris TOTAL dan


SUBTOTAL, selalu perhatikan nilai NULL karena nilai tersebut
akan selalu berada dibawah ketika diurutkan secara
descending

Selanjutnya sebagai latihan, ubah nilai NULL tersebut dengan nilai


string TOTAL dan SUB TOTAL

Silakan dicoba?

Apakah queryny sama seperti saya ini?

1. SELECT IFNULL(bln_trx, "TOTAL") AS bulan


2. , IFNULL(id, "SUB TOTAL") AS id
3. , nama
4. , jml_trx
5. , nilai_trx
6. FROM
7. (
8. SELECT *
9. FROM
10. (SELECT MONTH(tgl_trx) AS bln_trx
11. , id_pelanggan AS id
12. , IFNULL(nama, "-") AS nama
13. , COUNT(id_trx) AS jml_trx
14. , SUM(total_trx) AS nilai_trx
15. FROM penjualan
16. LEFT JOIN pelanggan USING (id_pelanggan)
17. WHERE YEAR(tgl_trx) = 2017
18. GROUP BY bln_trx, id_pelanggan

206 BAB 11 Menguasai WITH ROLLUP


19. WITH ROLLUP
20. ) AS penjualan
21. ORDER BY bln_trx DESC, id DESC
22. ) AS penjualan

Hasil:
+-------+-----------+------+---------+-----------+
| bulan | id | nama | jml_trx | nilai_trx |
+-------+-----------+------+---------+-----------+
| 4 | 2 | Beta | 1 | 110000 |
| 4 | 0 | - | 1 | 259000 |
| 4 | SUB TOTAL | Beta | 2 | 369000 |
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | SUB TOTAL | Alfa | 2 | 378000 |
| TOTAL | SUB TOTAL | Beta | 4 | 747000 |
+-------+-----------+------+---------+-----------+

Penjelasan: kita akan mengolah lagi tabel hasil query, sehingga mau
tidak mau kita harus menggunakan subquery

Real World….
Dalam dunia nyata, pengurutan data bisa sangat kompleks, misal
pada tabel hasil query diatas, selain terdapat baris total dan subtotal,
diata diurutkan lagi berdasarkan nilai_trx tertinggi

Bisakah dilakukan dengan SQL?

Sayang sekali tidak bisa… Kenapa? Karena SQL hanya bahasa


deklarasi (seperti HTML dan CSS) yang hanya ditujukan untuk
pengambilan data untuk dapat diolah lebih lanjut oleh aplikasi lain
bukan bahasa procedural seperti PHP dan ASP yang memang
ditujukan untuk menyelesaikan suatu masalah.

So… jika memang tidak bisa dikerjakan di SQL, tidak perlu


dipaksakan….

Killer Trik Query MySQL 207


11.4. Studi kasus
Sebagai contoh kasus, kita lanjutkan contoh pada bagian
sebelumnya sehingga tabel yang dihasikan lebih informatif dan siap
untuk disajikan, tabel hasil query sebelumnya adalah seperti ini:
+-------+-----------+------+---------+-----------+
| bulan | id | nama | jml_trx | nilai_trx |
+-------+-----------+------+---------+-----------+
| 4 | 2 | Beta | 1 | 110000 |
| 4 | 0 | - | 1 | 259000 |
| 4 | SUB TOTAL | Beta | 2 | 369000 |
| 3 | 1 | Alfa | 2 | 378000 |
| 3 | SUB TOTAL | Alfa | 2 | 378000 |
| TOTAL | SUB TOTAL | Beta | 4 | 747000 |
+-------+-----------+------+---------+-----------+

Nah, kita akan mengubahnya menjadi seperti ini:


+-----------+-------+---------+-----------+
| nama | bulan | jml_trx | nilai_trx |
+-----------+-------+---------+-----------+
| Beta | 4 | 1 | 110000 |
| - | 4 | 1 | 259000 |
| SUB TOTAL | 4 | 2 | 369000 |
| Alfa | 3 | 2 | 378000 |
| SUB TOTAL | 3 | 2 | 378000 |
| TOTAL | | 4 | 747000 |
+-----------+-------+---------+-----------+

Bagaimana querynya?

Silakan dicoba, cara yang digunakan sudah dibahas pada bab bab
sebelumnya….

Sebagai clue, kita gunakan ekspresi logika CASE

Sudah? Baik, mari kita cocokkan. Query versi saya adalah sebagai
berikut:

1. SELECT CASE WHEN bln_trx IS NULL THEN "TOTAL"


2. WHEN id IS NULL THEN "SUB TOTAL"

208 BAB 11 Menguasai WITH ROLLUP


3. ELSE nama
4. END AS nama
5. , IFNULL(bln_trx, "") AS bulan
6. , jml_trx
7. , nilai_trx
8. FROM
9. (
10. SELECT *
11. FROM
12. ( SELECT MONTH(tgl_trx) AS bln_trx
13. , id_pelanggan AS id
14. , IFNULL(nama, "-") AS nama
15. , COUNT(id_trx) AS jml_trx
16. , SUM(total_trx) AS nilai_trx
17. FROM penjualan
18. LEFT JOIN pelanggan USING (id_pelanggan)
19. WHERE YEAR(tgl_trx) = 2017
20. GROUP BY bln_trx, id_pelanggan
21. WITH ROLLUP
22. ) AS penjualan
23. ORDER BY bln_trx DESC, id DESC, nilai_trx DESC
24. ) AS penjualan

Ingat kembali pinsip eksekusi sql, bahwa pada statemen SELECT,


setiap baris pada tabel hasil klausa FROM akan di evaluasi sesuai
dengan yang ada pada klausa select, pehatikan ilustrasi berikut:

Gambar 11.2 Ilustrasi Eksekusi Klausa SELECT

Killer Trik Query MySQL 209


Pada contoh diatas, karena baris ketiga kolom id bernilai NULL, maka
memenuhi kondisi kedua pada ekspresi CASE, sehingga kolom nama
bernilai SUB TOTAL, demikian juga dengan kolom lain.

210 BAB 11 Menguasai WITH ROLLUP


BAB 12. Menguasai Variable
Pada kasus tertentu, kita perlu menggunakan hasil eksekusi suatu
query untuk kemudian digunakan pada query lain, untuk keperluan
tersebut, kita perlu menyimpan data hasil query. MySQL
memfasilitasi ini dengan menyediakan fitur bernama variabel.

Materi tentang variabel merupakan materi lanjutan, di buku ini kita


tidak membahasnya secara mendalam, initinya Anda paham variabel
dan dapat menggunakannya pada kasus kasus umum.

12.1. Memahami Variable


Variabel digunakan untuk menyimpan data, baik data scalar,
ekspresi, maupun data hasil query. Sesuai artinya yaitu berubah,
maka nilai variabel ini dapat berubah ubah.

Pada MySQL, variable dapat di deklarasikan menggunakan dua cara


yaitu menggunakan statement SET dan SELECT diikuti nama variable.

Contoh pendeklarasian variable:

1. SET @nomor := 1;
2. SET @total := (SELECT SUM(total_trx) FROM penjualan);
3. SELECT @nomor := 1;
4. SELECT @total := (SELECT SUM(total_trx) FROM penjualan);

Note: jika nilai variable berupa query, maka query tersebut harus
diletakkan di dalam tanda kurung (baris 2 dan 4)

Dalam satu statement, kita dapat mendeklarasikan beberapa


variable sekaligus, caranya, pisahkan variable tersebut dengan tanda
koma, misal:

Killer Trik Query MySQL 211


1. SET @nomor := 1, @total := (SELECT SUM(total_trx) FROM
penjualan);
2. SELECT @nomor := 1, SELECT @total := (SELECT SUM(total_trx)
FROM penjualan);

Ketentuan:

 Untuk meng assign nilai pada variabel, kita dapat menggunakan


tanda := atau =

 Tanda := dapat digunaan pada SET dan SELECT sedangkan =


hanya dapat digunakan pada SET. Untuk memudahkan, gunakan
selalu :=

 Nama variabel harus diawali dengan @ dan diikuti dengan: huruf,


angka, titik (.), underscore (_), atau dollar ($)

 Nama variabel bersifat case insensitive, tidak membedakan


huruf kapital, @Nomor akan sama dengan @nomor

 Variable hanya dapat diisi satu nilai. Jika nilai tersebut berupa
hasil query, maka data hasil query tersebut harus terdiri dari
satu nilai (satu kolom dan satu baris)

Sebagai contoh, misal kita urutkan data tabel penjualan berdasarkan


tanggal transaksi dan kita beri kolom nomor urut. Query yang kita
jalankan:

1. SET @no := 0;
2. SELECT @no := @no + 1 AS no_urut
3. , nama
4. , tgl_trx
5. FROM penjualan
6. LEFT JOIN pelanggan USING (id_pelanggan)
7. WHERE YEAR(tgl_trx) = 2017
8. ORDER BY tgl_trx

212 BAB 12. Menguasai Variable


Hasil:
+---------+------+------------+
| no_urut | nama | tgl_trx |
+---------+------+------------+
| 1 | Alfa | 2017-03-02 |
| 2 | Alfa | 2017-03-10 |
| 3 | Beta | 2017-04-05 |
| 4 | NULL | 2017-04-10 |
+---------+------+------------+

Pada query diatas, pertama tama kita deklarasikan variabel @no


dengan nilai 0, selanjutnya, pada statement SELECT, variabel tersebut
akan dieksekusi sebanyak jumlah baris yang ada pada tabel hasil
klausa FROM, ingat kembali konsep urutan eksekusi query, bahwa
ketik menjalankan klausa select, setiap baris tabel hasil klausa from
akan dieksekusi.

Setiap kali variabel tersebut dieksekusi, nilainya akan selalu


bertambah satu @no := @no + 1, sehingga akan membentuk data
nomor urut.

12.2. Single Statement


Pada contoh sebelumnya, kita mendefinisikan variabel
menggunakan statemen SET di luar statemen SELECT, sehingga
terdapat dua statemen yaitu SET dan SELECT.

Ketika praktik lapangan dimana query SQL kita terapkan pada


aplikasi, maka pendefinisian dua statement ini terkadang
merepotkan, untuk itu kita perlu mendefinisikan nya dengan satu
statemen (single statemen).

Dengan single statemen, maka query diatas akan berbentuk:

1. SELECT @no := @no + 1 AS no_urut

Killer Trik Query MySQL 213


2. , nama
3. , tgl_trx
4. FROM (SELECT @no := 0) AS nomor
5. , penjualan
6. LEFT JOIN pelanggan USING (id_pelanggan)
7. WHERE YEAR(tgl_trx) = 2017
8. ORDER BY tgl_trx

Hasil yang kita peroleh sama persis yaitu:


+---------+------+------------+
| no_urut | nama | tgl_trx |
+---------+------+------------+
| 1 | Alfa | 2017-03-02 |
| 2 | Alfa | 2017-03-10 |
| 3 | Beta | 2017-04-05 |
| 4 | NULL | 2017-04-10 |
+---------+------+------------+

Bagaimana proses pendefinisian variabel diatas? Ingat kembali


konsep urutan eksekusi query

Sudah paham kan?

Yup, seperti yang telah kita pelajari, bahwa pertama kali yang
dieksekusi adalah klausa FROM

1. FROM (SELECT @no := 0) AS nomor


2. , penjualan
3. LEFT JOIN pelanggan USING (id_pelanggan)

Nah, pada klausa tersebut, selain menggabungkan tabel penjualan


dan pelanggan, MySQL juga mendefinisikan variabel @no, nah
disitulah awal mula variabel @no didefinisikan dengan nilai 0

Dari sini sudah paham kan kelanjutannya bagaimana variabel @no


bisa berbentuk no urut?

214 BAB 12. Menguasai Variable


Yup, ketika mendefinisikan variabel pada statemen SELECT, yaitu
pada contoh diatas @no := @no + 1 maka selain menyimpan nilai
variabel, nilai variabel tersebut akan dicetak.

Sudah tahu kan bagaimana proses penyimpanan nilai variabel?

Prosesnya sama seperti bahasa pemrograman umum…

Ketika menyimpan variabel, ekspresi sebelah kanan tanda := akan di


eksekusi terlebih dahulu, hasilnya disimpan ke variabel di sebelah
kiri, sehingga pada eksekusi pertama nilai sebelah kanan adalah satu
(0 + 1) hasilnya disimpan pada variabel @no dan dicetak sehingga
muncul angka 1, demikian seterusnya hingga sampai eksekusi baris
terakhir.

Penggunaan single statemen akan lebih memudahkan dalam


pengembangan aplikasi karena lebih mudah mengeksekusi satu
statemen dari pada dua statemen, terlebih jika aplikasi kita
menggunakan framework (seperti codeigniter pada PHP).

Contoh penerapan single statement pada aplikasi berbasis PHP

1. <?php
2. $sql = 'SELECT @no := @no + 1 AS no_urut
3. , nama
4. , tgl_trx
5. FROM (SELECT @no := 0) AS nomor
6. , penjualan
7. LEFT JOIN pelanggan USING (id_pelanggan)
8. WHERE YEAR(tgl_trx) = 2017
9. ORDER BY tgl_trx';
10. $query = mysqli_query($sql);

Killer Trik Query MySQL 215


12.3. Studi Kasus #1
Sebagai contoh kita akan membuat report data penjualan per
pelanggan untuk transaksi tahun 2017 beserta persentase
kontribusinya terhadap penjualan.

Output yang kita harapkan adalah sebagai berikut:


+------+-----------+--------+------------+
| nama | nilai_trx | total | kontribusi |
+------+-----------+--------+------------+
| - | 259000 | 747000 | 34.67 |
| Alfa | 378000 | 747000 | 50.60 |
| Beta | 110000 | 747000 | 14.73 |
+------+-----------+--------+------------+

Bagaimana querynya?

Sejauh yang telah kita pelajari, maka kita akan menggunakan


subquery sebagai berikut:

1. SELECT IFNULL(nama, "-") AS nama


2. , SUM(total_trx) AS nilai_trx
3. , ( SELECT SUM(total_trx)
4. FROM penjualan
5. WHERE YEAR(tgl_trx) = 2017
6. ) AS total
7. , ROUND (SUM(total_trx) / ( SELECT SUM(total_trx)
8. FROM penjualan
9. WHERE YEAR(tgl_trx) = 2017
10. ) * 100, 2) AS kontribusi
11. FROM penjualan
12. LEFT JOIN pelanggan USING(id_pelanggan)
13. WHERE YEAR(tgl_trx) = 2017
14. GROUP BY id_pelanggan

Query diatas berjalan dengan baik dan menghasilkan output sesuai


dengan yang kita harapkan, namun jika diperhatikan, akan banyak
sekali eksekusi subquery (untuk mendapatkan total transaksi)
karena seperti konsep alur eksekusi query, klausa SELECT akan

216 BAB 12. Menguasai Variable


dieksekusi sebanyak baris tabel hasil klausa FROM, sehingga
subquery ikut dieksekusi sebanyak itu juga.

Nah untuk efisiensi, maka kita dapat menyimpan data subquery


tersebut kedalam variabel, kita ubah query menjadi berikut:

1. SELECT IFNULL(nama, "-") AS nama


2. , SUM(total_trx) AS nilai_trx
3. , ROUND(@total) AS total
4. , ROUND (SUM(total_trx) / @total * 100, 2)
5. AS kontribusi
6. FROM (SELECT @total := SUM(total_trx) AS total
7. FROM penjualan
8. WHERE YEAR(tgl_trx) = 2017
9. ) AS total
10. , penjualan
11. LEFT JOIN pelanggan USING(id_pelanggan)
12. WHERE YEAR(tgl_trx) = 2017
13. GROUP BY id_pelanggan

Hasil yang kita peroleh sama yaitu:


+------+-----------+--------+------------+
| nama | nilai_trx | total | kontribusi |
+------+-----------+--------+------------+
| - | 259000 | 747000 | 34.67 |
| Alfa | 378000 | 747000 | 50.60 |
| Beta | 110000 | 747000 | 14.73 |
+------+-----------+--------+------------+

12.4. Studi Kasus #2


Kasus berikutnya adalah kita akan menampilkan menampilkan data
akumulasi penjualan per bulan, dengan menampilkan banyaknya
transaksi perbulan total nilai transaksi per bulan, output yang kita
harapkan adalah:
+-------+---------+-----------+-----------+
| bulan | jml_trx | total_trx | akumulasi |
+-------+---------+-----------+-----------+
| 3 | 2 | 378000 | 378000 |
| 4 | 2 | 369000 | 747000 |

Killer Trik Query MySQL 217


| 11 | 1 | 256000 | 1003000 |
+-------+---------+-----------+-----------+

Pada tabel diatas, nilai pada kolom akumulasi akan ditambahkan


dengan nilai kolom total_trx pada baris berikutnya.

Bagaimana querynya?

Silakan dicoba dulu ya… practice make perfect… right?

Oiya tabel yang kita gunakan adalah tabel penjualan sebagai berikut:
+--------+--------------+------------+-----------+
| id_trx | id_pelanggan | tgl_trx | total_trx |
+--------+--------------+------------+-----------+
| 1 | 1 | 2017-03-02 | 192000 |
| 2 | 1 | 2017-03-10 | 186000 |
| 3 | 0 | 2017-04-10 | 259000 |
| 4 | 2 | 2017-04-05 | 110000 |
| 5 | 2 | 2016-11-10 | 256000 |
+--------+--------------+------------+-----------+

Sudah bisa?

Yup, mari kita cocokkan jawabannya.

Kalau versi saya, querynya adalah sebagai berikut:

1. SELECT bulan
2. , jml_trx
3. , total_trx
4. , @akm := @akm + total_trx AS akumulasi
5. FROM
6. ( SELECT @akm :=0) AS akumulasi,
7. ( SELECT MONTH(tgl_trx) AS bulan
8. , COUNT(id_trx) AS jml_trx
9. , SUM(total_trx) AS total_trx
10. FROM penjualan
11. GROUP BY MONTH(tgl_trx)
12. ) AS penjualan

Bagaimana prosesnya?

218 BAB 12. Menguasai Variable


Pertama kita identifikasi dulu permasalahannya dan diketahui
bahwa kita perlu membuat suatu variabel akumulatif dengan pola
sebagai berikut: @akumulasi = @akumulasi + total_trx

Pertama…
Seperti prinsip yang telah kita pahami, pertama tama kita definisikan
klausa FROM

Pada klausa FROM, pertama kita definisikan variabel @akumulasi


yang kita singkat dengan @akm

1. ( SELECT @akm :=0) AS akumulasi

selanjutnya kita buat tabel rekap per bulan

1. ( SELECT MONTH(tgl_trx) AS bulan


2. , COUNT(id_trx) AS jml_trx
3. , SUM(total_trx) AS total_trx
4. FROM penjualan
5. GROUP BY MONTH(tgl_trx)
6. ) AS penjualan

Hasilnya adalah sebagai berikut:


+-------+---------+-----------+
| bulan | jml_trx | total_trx |
+-------+---------+-----------+
| 3 | 2 | 378000 |
| 4 | 2 | 369000 |
| 11 | 1 | 256000 |
+-------+---------+-----------+

Kedua…
Sesuai prinsip yang kita pahami, kita definisikan klausa SELECT.
Kolom bulan, jml_trx, dan total_trx sudah ada pada tabel rekap
perbulan (tabel diatas), selanjutnya kita tinggal tambahkan kolom

Killer Trik Query MySQL 219


akumulasi dengan menambahkan @akm := @akm + total_trx AS
akumulasi

Hasilnya…
+-------+---------+-----------+-----------+
| bulan | jml_trx | total_trx | akumulasi |
+-------+---------+-----------+-----------+
| 3 | 2 | 378000 | 378000 |
| 4 | 2 | 369000 | 747000 |
| 11 | 1 | 256000 | 1003000 |
+-------+---------+-----------+-----------+

Bagaimana dengan query Anda? Sudah sama kan?

220 BAB 12. Menguasai Variable


BAB 13 Menguasai DATE TIME
Pada BAB 6 Mengusai Fungsi Scalar, kita telah membahas tentang
fungsi DATE dan TIME. Pada bab tersebut telah disampaikan contoh
sederhana penggunaan fungsi DATE dan TIME. Pada bab ini kita
akan membahas berbagai contoh kasus terkait date dan time,
dengan demikian Anda akan memiliki gambaran penerapan date
dan time di lapangan.

13.1. Studi Kasus #1


Pada contoh kasus kali ini, kita akan membahas tentang beda waktu.

Misal kita memiliki tabel buku yang berisi daftar buku yang terdapat
di sebuah perpustakaan dan tabel buku_pinjam yang berisi data
buku yang dipinjam. Data pada tabel buku adalah sebagai berikut:
+---------+--------------------------+----------+
| id_buku | judul_buku | kategori |
+---------+--------------------------+----------+
| 1 | Belajar Database | 1 |
| 2 | Belajar PHP dan MySQL | 2 |
| 3 | Query MySQL untuk Pemula | 1 |
+---------+--------------------------+----------+

Sedangkan pada tabel buku_pinjam adalah sebagai berikut:


+-----------+---------+--------+------------+-------------+
| id_pinjam | id_buku | id_mhs | tgl_pinjam | tgl_kembali |
+-----------+---------+--------+------------+-------------+
| 1 | 1 | 1 | 2017-05-13 | 0000-00-00 |
| 2 | 2 | 2 | 2017-05-10 | 0000-00-00 |
| 3 | 3 | 1 | 2017-05-12 | 2017-05-15 |
| 4 | 4 | 3 | 2017-05-14 | 0000-00-00 |
| 5 | 3 | 4 | 2017-05-12 | 2017-05-14 |
+-----------+---------+--------+------------+-------------+

Killer Trik Query MySQL 221


Selanjutnya kita ingin menampilkan report data buku yang dipinjam
dan yang sudah kembali beserta lama peminjaman, dengan output
sebagai berikut:
+---------+------------+-------------+-------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam |
+---------+------------+-------------+-------------+
| 3 | 2017-05-12 | 2017-05-15 | 3 |
| 3 | 2017-05-12 | 2017-05-14 | 2 |
+---------+------------+-------------+-------------+

Bagaimana querynya?

Pertama… kita definisikan tabel hasil klausa FROM.

Jika melihat kolom pada tabel output (id_buku, tgl_pinjm, dan


tgl_kembali), maka kita hanya perlu satu tabel, yaitu tabel
buku_pinjam, sedangkan untuk kolom lama_pinjam tidak ada pada
kedua tabel, maka kita perlu mendefinisikannya pada klausa SELECT

Kedua… kita definisikan kolom pada klausa SELECT. Pada tabel


output terdapat kolom tambahan yaitu kolom lama_pinjam yang
berisi nilai beda waktu antara kolom tgl_pinjam dan tgl_kembali,
untuk itu kita gunakan fungsi DATEDIFF.

Bentuk query jadinya adalah sebagai berikut:

1. SELECT id_buku,
2. tgl_pinjam,
3. tgl_kembali,
4. DATEDIFF (tgl_kembali, tgl_pinjam) AS lama_pinjam
5. FROM buku_pinjam
6. WHERE tgl_kembali != "0000-00-00"

Hasil yang kita peroleh:


+---------+------------+-------------+-------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam |
+---------+------------+-------------+-------------+
| 3 | 2017-05-12 | 2017-05-15 | 3 |

222 BAB 13 Menguasai DATE TIME


| 3 | 2017-05-12 | 2017-05-14 | 2 |
+---------+------------+-------------+-------------+

Pengayaan…
Selanjutnya mari kita tambahkan kolom status dan kolom jml_sanksi
dengan kriteria:

 Jika lama peminjaman lebih dari 2 hari, maka status terlambat.

 Jika terlambat, maka dikenakan sanksi Rp. 1.500 per hari

Tabel outputnya adalah sebagai berikut:


+---------+------------+-------------+-------------+-----------------+------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam | status | jml_sanksi |
+---------+------------+-------------+-------------+-----------------+------------+
| 3 | 2017-05-12 | 2017-05-15 | 3 | Terlambat | 1500 |
| 3 | 2017-05-12 | 2017-05-14 | 2 | Tidak Terlambat | 0 |
+---------+------------+-------------+-------------+-----------------+------------+

Bagaimana querynya?

Penyusunan querynya sama persis dengan sebelumnya. Pada


klausa FROM, kita hanya perlu satu tabel yaitu tabel buku_pinjam,
kenapa? Ya karena kolom yang kita perlukan (bukan kolom
tambahan) hanya ada pada tabel tersebut.

Selanjutnya pada klausa SELECT kita definisikan kolom


tambahannya yaitu kolom status dan jml_sanksi. Karena pada
kedua kolom tersebut kita mendefinisikan kondisi "Jika…" maka kita
gunakan fungsi IF.

Bentuk query jadinya adalah sebagai berikut:

1. SELECT id_buku,
2. tgl_pinjam,
3. tgl_kembali,
4. DATEDIFF (tgl_kembali, tgl_pinjam) AS lama_pinjam,
5. IF (DATEDIFF (tgl_kembali, tgl_pinjam) > 2
6. , "Terlambat", "Tidak Terlambat"

Killer Trik Query MySQL 223


7. ) AS status,
8. IF (DATEDIFF (tgl_kembali, tgl_pinjam) > 2,
9. 1500 * (DATEDIFF (tgl_kembali, tgl_pinjam) - 2),
10. 0) AS jml_sanksi
11. FROM buku_pinjam
12. WHERE tgl_kembali != "0000-00-00"

Hasil yang kita peroleh:


+---------+------------+-------------+-------------+-----------------+------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam | status | jml_sanksi |
+---------+------------+-------------+-------------+-----------------+------------+
| 3 | 2017-05-12 | 2017-05-15 | 3 | Terlambat | 1500 |
| 3 | 2017-05-12 | 2017-05-14 | 2 | Tidak Terlambat | 0 |
+---------+------------+-------------+-------------+-----------------+------------+

Pada contoh diatas, proses pembuatan kolom status dan jml_sanksi


tidak bisa menggunakan kolom alias "lama_pinjam", melainkan harus
menggunakan kolom tgl_kembali dan tgl_pinjam, kenapa? karena
seperti yang telah kita pelajari, kolom alias ini baru tersedia setelah
klausa SELECT selesai dieksekusi, sehingga baru dapat digunakan
pada klausa GROUP BY

Menggunakan kolom alias…

Melanjutkan contoh diatas, kali ini kita gunakan kolom alias untuk
membuat kolom status dan jml_sanksi

Bagaimana querynya?

Untuk dapat menggunakan kolom alias, maka kolom tersebut harus


sudah terbentuk, untuk itu kita perlu menggunakan subquery. Query
yang kita jalankan:

1. SELECT id_buku,
2. tgl_pinjam,
3. tgl_kembali,
4. lama_pinjam,
5. IF (lama_pinjam > 2,"Terlambat","Tidak Terlambat") AS
status,

224 BAB 13 Menguasai DATE TIME


IF (lama_pinjam > 2,1500 * (lama_pinjam - 2),0) AS
6. jml_sanksi
FROM (
7. SELECT *,
8. DATEDIFF (tgl_kembali, tgl_pinjam) AS lama_pinjam
9. FROM buku_pinjam
10. ) AS buku_pinjam
11. WHERE tgl_kembali != "0000-00-00"

Pada contoh diatas, subquery pada klausa FROM akan membentuk


temporary tabel sebagai berikut:
+-----------+---------+--------+------------+-------------+-------------+
| id_pinjam | id_buku | id_mhs | tgl_pinjam | tgl_kembali | lama_pinjam |
+-----------+---------+--------+------------+-------------+-------------+
| 1 | 1 | 1 | 2017-05-13 | 0000-00-00 | NULL |
| 2 | 2 | 2 | 2017-05-10 | 0000-00-00 | NULL |
| 3 | 3 | 1 | 2017-05-12 | 2017-05-15 | 3 |
| 4 | 4 | 3 | 2017-05-14 | 0000-00-00 | NULL |
| 5 | 3 | 4 | 2017-05-12 | 2017-05-14 | 2 |
+-----------+---------+--------+------------+-------------+-------------+

Selanjutnya, tabel tersebut kita gunakan pada query utama.

13.2. Studi Kasus #2


Masih menggunakan tabel buku dan buku_pinjam yang kita gunakan
pada studi kasus sebelumnya, kali ini kita tampilkan data buku yang
dipinjam tetapi belum dikembalikan beserta jumlah sanksinya.

Tabel outputnya adalah sebagai berikut:


+---------+------------+-------------+-------------+------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam | jml_sanksi |
+---------+------------+-------------+-------------+------------+
| 1 | 2017-05-13 | 0000-00-00 | 1 | 0 |
| 2 | 2017-05-10 | 0000-00-00 | 4 | 3000 |
| 4 | 2017-05-14 | 0000-00-00 | 0 | 0 |
+---------+------------+-------------+-------------+------------+

Bagaimana querynya?

Killer Trik Query MySQL 225


Querynya sama seperti pada kasus sebelumnya, tinggal kita
tambahkan klausa WHERE untuk memfilter data berdasarkan
tanggal kembali.

Querynya adalah sebagai berikut:

1. SELECT id_buku,
2. tgl_pinjam,
3. tgl_kembali,
4. lama_pinjam,
5. IF (lama_pinjam > 2,1500 * (lama_pinjam - 2),0) AS
jml_sanksi
6. FROM (
7. SELECT *,
8. DATEDIFF (NOW(), tgl_pinjam) AS lama_pinjam
9. FROM buku_pinjam
10. ) AS buku_pinjam
11. WHERE tgl_kembali = "0000-00-00"

Hasil yang kita peroleh:


+---------+------------+-------------+-------------+------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam | jml_sanksi |
+---------+------------+-------------+-------------+------------+
| 1 | 2017-05-13 | 0000-00-00 | 1 | 0 |
| 2 | 2017-05-10 | 0000-00-00 | 4 | 3000 |
| 4 | 2017-05-14 | 0000-00-00 | 0 | 0 |
+---------+------------+-------------+-------------+------------+

Query diatas sama seperti query sebelumnya, namun dengan sedikit


perubahan, yaitu kita ubah tgl_kembali menjadi NOW(), selain itu
pada bagian WHERE kita ubah != menjadi =

Untuk latihan, coba filter data diatas dengan hanya menampilkan


data buku yang sudah dipinjam lebih dari 2 hari

Lebih lanjut…
Selanjutnya, kita persempit lagi kriteria yaitu untuk buku dengan
kategori 1, maka lama peminjaman hanya boleh 1 hari, jika lebih dari
itu, akan dikenakan sanksi.

226 BAB 13 Menguasai DATE TIME


Query yang kita jalankan:

1. SELECT id_buku,
2. tgl_pinjam,
3. tgl_kembali,
4. lama_pinjam,
5. id_kategori,
6. CASE WHEN (lama_pinjam > 1 AND id_kategori = 1)
7. OR
8. (lama_pinjam > 2 AND id_kategori != 1)
9. THEN "Terlambat"
10. ELSE "Tidak Terlambat"
11. END AS status,
12. CASE WHEN lama_pinjam > 1 AND id_kategori = 1
13. THEN 2000 * (lama_pinjam - 1)
14. WHEN lama_pinjam > 2 AND id_kategori != 1
15. THEN 1500 * (lama_pinjam - 2)
16. ELSE 0
17. END AS jml_sanksi
18. FROM (
19. SELECT *,
20. DATEDIFF (tgl_kembali, tgl_pinjam) AS lama_pinjam
21. FROM buku_pinjam
22. ) AS buku_pinjam
23. LEFT JOIN buku USING (id_buku)
24. WHERE tgl_kembali != "0000-00-00"

Hasil yang kita peroleh:


+---------+------------+-------------+-------------+-------------+-----------+------------+
| id_buku | tgl_pinjam | tgl_kembali | lama_pinjam | id_kategori | status | jml_sanksi |
+---------+------------+-------------+-------------+-------------+-----------+------------+
| 3 | 2017-05-12 | 2017-05-15 | 3 | 1 | Terlambat | 4000 |
| 3 | 2017-05-12 | 2017-05-14 | 2 | 1 | Terlambat | 2000 |
+---------+------------+-------------+-------------+-------------+-----------+------------+

Pada query diatas, kita menggunakan CASE … WHEN karena kondisi


yang kita uji lebih dari satu, selain itu kita gunakan klausa JOIN untuk
menggabungkan tabel buku_pinjam dengan tabel buku.

13.3. Studi Kasus #3


Pada studi kasus kali ini, kita akan membahas contoh contoh query
terkait waktu, diantaranya menghitung perbedaan waktu, waktu

Killer Trik Query MySQL 227


maksimal, minimal, dan waktu rata-rata. Misal kita memiliki tabel
absensi sebagai berikut:
+----------+------------+---------------------+
| id_absen | id_pegawai | waktu_absen |
+----------+------------+---------------------+
| 1 | 1 | 2017-05-14 07:22:59 |
| 2 | 1 | 2017-05-14 07:27:12 |
| 3 | 2 | 2017-05-14 07:29:25 |
| 4 | 3 | 2017-05-14 07:31:01 |
| 5 | 4 | 2017-05-14 07:59:07 |
| 6 | 5 | 2017-05-14 08:00:00 |
| 7 | 6 | 2017-05-14 08:01:00 |
| 8 | 7 | 2017-05-14 12:00:00 |
| 9 | 8 | 2017-05-14 12:01:00 |
| 10 | 2 | 2017-05-14 17:01:00 |
| 11 | 3 | 2017-05-14 17:05:00 |
| 12 | 2 | 2017-05-14 17:15:00 |
| 13 | 1 | 2017-05-14 17:17:04 |
| 14 | 4 | 2017-05-14 16:00:00 |
| 15 | 5 | 2017-05-14 17:20:04 |
| 17 | 7 | 2017-05-14 17:25:24 |
| 18 | 8 | 2017-05-14 17:26:27 |
+----------+------------+---------------------+

Selanjutnya kita tampilkan data pegawai beserta jam absen


berangkat dan pulang, sehingga diperoleh tabel output sebagai
berikut:
+------------+-----------+------------+
| id_pegawai | jam_masuk | jam_pulang |
+------------+-----------+------------+
| 1 | 07:22:59 | 17:17:04 |
| 2 | 07:29:25 | 17:15:00 |
| 3 | 07:31:01 | 17:05:00 |
| 4 | 07:59:07 | 16:00:00 |
| 5 | 08:00:00 | 17:20:04 |
| 6 | 08:01:00 | - |
| 7 | 12:00:00 | 17:25:24 |
| 8 | - | 17:26:27 |
+------------+-----------+------------+

Bagaimana Querynya?

Biasanya query yang kita buat adalah sebagai berikut:

228 BAB 13 Menguasai DATE TIME


1. SELECT id_pegawai,
2. TIME( MIN(waktu_absen) ) AS jam_masuk,
3. TIME( MAX(waktu_absen) ) AS jam_pulang
4. FROM absensi
5. GROUP BY id_pegawai

Pada query diatas, kita gunakan fungsi TIME untuk mengambil data
waktu saja tanpa tanggal (hh:mm:ss), fungsi MIN untuk mengambil
waktu terkecil dan fungsi MAX untuk waktu terbesar.

Bagaimana hasilnya? Hasilnya adalah sebagai berikut:


+------------+-----------+------------+
| id_pegawai | jam_masuk | jam_pulang |
+------------+-----------+------------+
| 1 | 07:22:59 | 17:17:04 |
| 2 | 07:29:25 | 17:15:00 |
| 3 | 07:31:01 | 17:05:00 |
| 4 | 07:59:07 | 16:00:00 |
| 5 | 08:00:00 | 17:20:04 |
| 6 | 08:01:00 | 08:01:00 |
| 7 | 12:00:00 | 17:25:24 |
| 8 | 12:01:00 | 17:26:27 |
+------------+-----------+------------+

Dari tabel diatas terlihat bahwa untuk pegawai dengan id 6


jam_pulangnya 08:01:00, dan pegawai dengan id 8 masuknya jam
12:01:00, hal ini tentu saja tidak sesuai dengan kondisi riil, kondisi
riilnya, pegawai dengan id 6 tidak absen pulang, sedangkan pegawai
dengan id 8 tidak absen masuk.

Kenapa demikian? Karena fungsi MAX dan MIN tidak memandang


tanggal absennya dan tidak ada batasan jam absen.

Untuk itu, kita perlu batasi jam absen masuk dan jam absen pulang,
misal kita batasi jam absen masuk maksimal sebelum jam 12:00 dan
jam absen pulang minimal diatas jam 12:00. Query yang kita
jalankan:

Killer Trik Query MySQL 229


1. SELECT id_pegawai,
2. IF (TIME( MIN(waktu_absen) ) <= '12:00:00'
3. , TIME( MIN(waktu_absen) )
4. , '-'
5. ) AS jam_masuk,
6. IF (TIME( MAX(waktu_absen) ) > '12:00:00'
7. , TIME( MAX(waktu_absen) )
8. , '-'
9. )AS jam_pulang
10. FROM absensi
11. GROUP BY id_pegawai

Pada query diatas, pertama-tama MySQL akan mengeksekusi klausa


FORM sehingga diperoleh tabel absensi (tabel riil)

Selanjutnya MySQL akan mengeksekusi klausa SELECT, karena


terdapat fungsi agregasi, MAX dan MIN, maka kedua fungsi ini akan
dieksekusi bersama sama dengan klausa GROUP BY

Lebih Lanjut…
Selanjutnya kita buat kriteria baru, dimana jika absen sebelum jam
7:31 berarti belum terlambat, 07:31 s.d 08:00 Terlambat kategori I
(TLI), 08:01 s.d 12:00 terlambat kategori II (TLII), selanjutnya jika
absen sebelum jam 17:00 termasuk kategori pulang sebelum
waktunya (PSW).

Tabel outputnya adalah sebagai berikut:


+------------+-----------+-----------+------------+------------+
| id_pegawai | jam_masuk | ket_masuk | jam_pulang | ket_pulang |
+------------+-----------+-----------+------------+------------+
| 1 | 07:22:59 | - | 17:17:04 | - |
| 2 | 07:29:25 | - | 17:15:00 | - |
| 3 | 07:31:01 | TLI | 17:05:00 | - |
| 4 | 07:59:07 | TLI | 16:00:00 | PSW |
| 5 | 08:00:00 | TLI | 17:20:04 | - |
| 6 | 08:01:00 | TLII | - | - |
| 7 | 12:00:00 | TLII | 17:25:24 | - |
| 8 | - | - | 17:26:27 | - |
+------------+-----------+-----------+------------+------------+

230 BAB 13 Menguasai DATE TIME


Bagaimana querynya?

Baiklah langsung saja, query yang kita jalankan:

1. SELECT id_pegawai,
2. jam_masuk,
3. CASE WHEN jam_masuk != "-"
4. THEN
5. CASE WHEN jam_masuk > "07:30:00"
6. AND jam_masuk <= "08:00:00"
7. THEN "TLI"
8. WHEN jam_masuk > "08:00:00"
9. AND jam_masuk <= "12:00:00"
10. THEN "TLII"
11. ELSE "-"
12. END
13. ELSE "-"
14. END AS ket_masuk,
15. jam_pulang,
16. CASE WHEN jam_pulang != "-" AND jam_pulang <"17:00:00"
17. THEN "PSW"
18. ELSE "-"
19. END AS ket_pulang
20. FROM (
21. SELECT *,
22. IF (TIME( MIN(waktu_absen) ) <= "12:00:00"
23. , TIME( MIN(waktu_absen) )
24. , "-"
25. ) AS jam_masuk,
26. IF (TIME( MAX(waktu_absen) ) > "12:00:00"
27. , TIME( MAX(waktu_absen) )
28. , "-"
29. )AS jam_pulang
30. FROM absensi
31. GROUP BY id_pegawai
32. ) AS absensi

Pada contoh diatas, pertama tama MySQL akan mengeksekusi


klausa FROM sehingga diperoleh tabel sama seperti pada studi
kasus sebelumnya:
+------------+-----------+------------+
| id_pegawai | jam_masuk | jam_pulang |
+------------+-----------+------------+
| 1 | 07:22:59 | 17:17:04 |

Killer Trik Query MySQL 231


| 2 | 07:29:25 | 17:15:00 |
| 3 | 07:31:01 | 17:05:00 |
| 4 | 07:59:07 | 16:00:00 |
| 5 | 08:00:00 | 17:20:04 |
| 6 | 08:01:00 | - |
| 7 | 12:00:00 | 17:25:24 |
| 8 | - | 17:26:27 |
+------------+-----------+------------+

Selanjutnya MySQL mengeksekusi klausa SELECT.

Seperti yang telah kita pelajari, ketika mengeksekusi klausa SELECT,


maka setiap baris hasil klausa FROM akan dievaluasi

Untuk mendapatkan nilai ket_masuk, nilai kolom jam_masuk akan


dievaluasi, jika tidak bernilai '-' yang berarti ada jam masuk, tes jam
tersebut apakah masuk kriteria TLI dan TLII, jika tidak termasuk
keduanya, isi dengan nilai '-'.

Selanjutnya, untuk mendapatkan keterangan waktu pulang


(ket_pulang), nilai kolom jam_pulang akan devaluasi, jika < 17:00:00
masuk kategori PSW, jika tidak, beri nilai '-'.

13.4. Studi Kasus #4


Studi kasus kali ini mirip dengan studi kasus #3, bedanya, kali ini kita
menggunakan data absensi namun untuk pegawai jaga malam.

Pada pegawai jaga malam, absen masuk dimulai pada sore atau
malam hari, sedangkan untuk absen pulang pada hari berikutnya

Tabel yang akan kita gunakan adalah tabel absensi_malam. Isi dari
tabel tersebut adalah sebagai berikut:
+----------+------------+---------------------+
| id_absen | id_pegawai | waktu_absen |
+----------+------------+---------------------+
| 1 | 1 | 2017-05-14 16:50:59 |
| 2 | 1 | 2017-05-14 16:55:12 |

232 BAB 13 Menguasai DATE TIME


| 3 | 2 | 2017-05-14 16:59:25 |
| 4 | 3 | 2017-05-14 17:01:00 |
| 5 | 4 | 2017-05-14 17:31:00 |
| 6 | 5 | 2017-05-14 21:01:00 |
| 7 | 2 | 2017-05-15 07:01:00 |
| 8 | 3 | 2017-05-15 07:05:00 |
| 9 | 2 | 2017-05-15 07:15:00 |
| 10 | 1 | 2017-05-15 07:17:04 |
| 11 | 5 | 2017-05-15 07:26:27 |
| 12 | 2 | 2017-05-15 16:47:00 |
| 13 | 3 | 2017-05-15 16:30:04 |
+----------+------------+---------------------+

Selanjutnya kita akan merekap waktu kehadiran dan waktu pulang


untuk tiap-tiap pegawai dengan ketentuan:

 Absen hadir paling cepat pukul 15:00:00 dan paling lambat


pukul 21:00:00

 Absen pulang adalah hari berikutnya, paling cepat pukul


05:00:00 dan paling lama pukul 09:00:00

Misal untuk tanggal 14-05-2017, absen masuk paling lambat pukul


21:00:00 dan untuk absen pulangnya adalah tanggal 15-05-2017,
paling cepat pukul 05:00:00.

Tabel output yang diinginkan adalah sebagai berikut:


+------------+------------+---------------------+---------------------+
| id_pegawai | tanggal | waktu_masuk | waktu_pulang |
+------------+------------+---------------------+---------------------+
| 1 | 2017-05-14 | 2017-05-14 16:55:12 | 2017-05-15 07:17:04 |
| 1 | 2017-05-15 | - | - |
| 2 | 2017-05-14 | 2017-05-14 16:59:25 | 2017-05-15 07:01:00 |
| 2 | 2017-05-15 | 2017-05-15 16:47:00 | - |
| 3 | 2017-05-14 | 2017-05-14 17:01:00 | 2017-05-15 07:05:00 |
| 3 | 2017-05-15 | 2017-05-15 16:30:04 | - |
| 4 | 2017-05-14 | 2017-05-14 17:31:00 | - |
| 5 | 2017-05-14 | - | 2017-05-15 07:26:27 |
| 5 | 2017-05-15 | - | - |
+------------+------------+---------------------+---------------------+

Bagaimana querynya?

Killer Trik Query MySQL 233


Baiklah langsung saja, querynya adalah sebagai berikut:

1. SELECT id_pegawai,
2. DATE(waktu_absen) AS tanggal,
3. IF (TIME( MAX(waktu_absen) ) <= "21:00:00"
4. AND TIME( MAX(waktu_absen) ) >= "15:00:00"
5. , MAX(waktu_absen)
6. , "-"
7. ) AS waktu_masuk,
8. (SELECT
9. IF (TIME( MIN(waktu_absen) ) > "05:00:00"
10. AND TIME( MIN(waktu_absen) ) < "09:00:00"
11. , MIN(waktu_absen)
12. , "-"
13. ) AS waktu_pulang
14. FROM absensi_malam am
15. WHERE DATE(waktu_absen)
16. = DATE(am1.waktu_absen) + INTERVAL 1 DAY
17. AND am1.id_pegawai = am.id_pegawai
18. ) AS waktu_pulang
19. FROM absensi_malam am1
20. GROUP BY id_pegawai, DATE(waktu_absen)

Perhatikan bahwa pada query diatas, kita menggunakan subquery


untuk membuat kolom waktu_pulang. Pada subquery tersebut, pada
bagian WHERE kita menggunakan INTERVAL 1 DAY untuk
mendapatkan tanggal berikutnya dari tangal berjalan
DATE(am1.waktu_absen) yang berasal dari tabel am1 yang ada pada
query utama.

Subquery tersebut termasuk jenis correlated subquery (subquery


berkorelasi), dimana subquery bergantung pada query utama
(kolom id_pegawai pada tabel alias am1). Lihat kembali pembahasan
mengenai bab subquery.

234 BAB 13 Menguasai DATE TIME


Untuk lebih memahami alur query diatas, perhatikan ilustrasi
berikut:

Gambar 13.1 Penjelasan Alur Query

Lebih Lanjut…
Selanjutnya, seperti pada studi kasus sebelumnya, kita buat
keterangan untuk keterlambatan absen, yaitu:

Killer Trik Query MySQL 235


 Untuk absen masuk, jika absen s.d pukul 17:30:00 maka diberi
keterangan TL1, jika absen antara pukul 17:31:00 s.d 21:00:00
diberi keterangan TL2.

 Untuk absen pulang, jika absen sebelum pukul 07:00:00 maka


diberi keterangan PSW.

Bentuk tabel outputnya adalah sebagai berikut:


+------------+------------+---------------------+-------------+---------------------+-------------+
| id_pegawai | tanggal | waktu_masuk | ket_masuk | waktu_pulang | ket_pulang |
+------------+------------+---------------------+-------------+---------------------+-------------+
| 1 | 2017-05-14 | 2017-05-14 16:55:12 | - | 2017-05-15 07:17:04 | - |
| 1 | 2017-05-15 | - | TIDAK ABSEN | - | TIDAK ABSEN |
| 2 | 2017-05-14 | 2017-05-14 16:59:25 | - | 2017-05-15 07:01:00 | - |
| 2 | 2017-05-15 | 2017-05-15 16:47:00 | - | - | TIDAK ABSEN |
| 3 | 2017-05-14 | 2017-05-14 17:01:00 | TL1 | 2017-05-15 07:05:00 | - |
| 3 | 2017-05-15 | 2017-05-15 16:30:04 | - | - | TIDAK ABSEN |
| 4 | 2017-05-14 | 2017-05-14 17:31:00 | TL2 | - | TIDAK ABSEN |
| 5 | 2017-05-14 | - | TIDAK ABSEN | 2017-05-15 07:26:27 | - |
| 5 | 2017-05-15 | - | TIDAK ABSEN | - | TIDAK ABSEN |
+------------+------------+---------------------+-------------+---------------------+-------------+

Bagaimana Querynya?

Baiklah, langsung saja. Querynya adalah sebagai berikut:

1. SELECT id_pegawai,
2. tanggal,
3. waktu_masuk,
4. CASE WHEN waktu_masuk != "-"
5. THEN
6. CASE WHEN TIME(waktu_masuk) > "17:00:00"
7. AND TIME(waktu_masuk) <= "17:31:00"
8. THEN "TL1"
9. WHEN TIME(waktu_masuk) > "17:31:00"
10. AND TIME(waktu_masuk) <= "21:00:00"
11. THEN "TL2"
12. ELSE "-"
13. END
14. ELSE "TIDAK ABSEN"
15. END AS ket_masuk,
16. waktu_pulang,
17. CASE WHEN waktu_pulang = "-"
18. THEN "TIDAK ABSEN"
19. ELSE
20. IF (TIME(waktu_pulang) < "07:00:00", "PSW", "-")
21. END AS ket_pulang

236 BAB 13 Menguasai DATE TIME


22. FROM (
23. SELECT *,
24. DATE(waktu_absen) AS tanggal,
25. IF (TIME( MAX(waktu_absen) ) <= "21:00:00"
26. AND TIME( MAX(waktu_absen) ) > "15:00:00"
27. , MAX(waktu_absen)
28. , "-"
29. ) AS waktu_masuk,
30. (SELECT
31. IF (TIME( MIN(waktu_absen) ) > "05:00:00"
32. AND TIME( MIN(waktu_absen) ) < "09:00:00"
33. , MIN(waktu_absen)
34. , "-"
35. ) AS waktu_pulang
36. FROM absensi_malam am
37. WHERE DATE(waktu_absen)
38. = DATE(am1.waktu_absen) + INTERVAL 1 DAY
39. AND am1.id_pegawai = am.id_pegawai
40. ) AS waktu_pulang
41. FROM absensi_malam am1
42. GROUP BY id_pegawai, DATE(waktu_absen)
43. ) AS absensi

Pada query diatas, subquery yang ada pada klausa FROM sama
persis dengan query sebelumnya yang menghasilkan tabel:
+------------+------------+---------------------+---------------------+
| id_pegawai | tanggal | waktu_masuk | waktu_pulang |
+------------+------------+---------------------+---------------------+
| 1 | 2017-05-14 | 2017-05-14 16:55:12 | 2017-05-15 07:17:04 |
| 1 | 2017-05-15 | - | - |
| 2 | 2017-05-14 | 2017-05-14 16:59:25 | 2017-05-15 07:01:00 |
| 2 | 2017-05-15 | 2017-05-15 16:47:00 | - |
| 3 | 2017-05-14 | 2017-05-14 17:01:00 | 2017-05-15 07:05:00 |
| 3 | 2017-05-15 | 2017-05-15 16:30:04 | - |
| 4 | 2017-05-14 | 2017-05-14 17:31:00 | - |
| 5 | 2017-05-14 | - | 2017-05-15 07:26:27 |
| 5 | 2017-05-15 | - | - |
+------------+------------+---------------------+---------------------+

Selanjutnya pada klausa SELECT, kita tambahkan kolom ket_masuk


dan ket_pulang berdasarkan tabel hasil klausa FROM (tabel diatas)

Pada ket_masuk kita tes apakah waktu masuk tidak bernilai strip ( - ):

Killer Trik Query MySQL 237


 Jika ya, maka dengan ekspresi CASE dan fungsi TIME
TIME(waktu_masuk) kita tes nilai jam masuk untuk mendapatkan
keterangan TL1, TL2, atau –

 Jika tidak, maka beri keterangan TIDAK ABSEN

Pada ket_pulang, kita tes apakah kolom waktu pulang bernilai strip (-
), jika ya maka beri keterangan TIDAK ABSEN, jika tidak maka dengan
fungsi IF, kita tes nilai waktu pulang, jika kurang dari pukul 07:00:00
maka beri keterangan PSW, jika tidak beri keterangan strip (-).

Perhatikan ilustrasi berikut:

Gambar 13.2. Ilustrasi Eksekusi Klausa SELECT

238 BAB 13 Menguasai DATE TIME


13.5. Studi Kasus #5
Pada studi kasus kali ini kita akan mencari waktu tercepat, terlambat,
dan waktu rata-rata dari pelari.

Sebagai contoh misal kita memiliki tabel pelari yang berisi catatan
waktu pencapaian berlari. Adapun isi dari tabel pelari tersebut
adalah sebagai berikut:
+------+---------+-----------+------------+
| id | nama | percobaan | waktu_lari |
+------+---------+-----------+------------+
| 1 | Alfa | 1 | 00:00:35 |
| 2 | Alfa | 2 | 00:00:30 |
| 3 | Alfa | 3 | 00:00:29 |
| 4 | Beta | 1 | 00:00:33 |
| 5 | Beta | 2 | 00:00:31 |
| 6 | Beta | 3 | 00:00:30 |
| 7 | Charlie | 1 | 00:00:29 |
| 8 | Charlie | 2 | 00:00:28 |
| 9 | Charlie | 3 | 00:00:31 |
+------+---------+-----------+------------+

Selanjutnya kita akan menampilkan data pelari, waktu minimal,


waktu maksimal, dan waktu rata ratanya, dengan bentuk tabel
output sebagai berikut:
+---------+---------------+----------------+-----------------+
| nama | waktu_minimal | waktu_maksimal | waktu_rata_rata |
+---------+---------------+----------------+-----------------+
| Alfa | 00:00:29 | 00:00:35 | 31.3333 |
| Beta | 00:00:30 | 00:00:33 | 31.3333 |
| Charlie | 00:00:28 | 00:00:31 | 29.3333 |
+---------+---------------+----------------+-----------------+

Bagaimana querynya?

Silakan dicoba, gampang kok

Yup. querynya simpel seperti ini:

Killer Trik Query MySQL 239


1. SELECT nama, MIN(waktu_lari) AS waktu_minimal,
2. MAX(waktu_lari) AS waktu_maksimal,
3. AVG(waktu_lari) AS waktu_rata_rata
4. FROM pelari
5. GROUP BY nama

Pada query diatas kita sudah sangat familiar dengan fungsi MIN,
MAX, dan AVG, sehingga mudah dipahami bagai mana cara kerja dan
alur query diatas.

Lebih lanjut…
Lebih lanjut, kita cari waktu minimal dan maksimal tersebut
diperoleh dari percobaan ke berapa. Tabel output yang kita inginkan
adalah sebagai berikut:
+---------+-----------+---------+------------+---------+-----------------+
| nama | waktu_min | perc_ke | waktu_maks | perc_ke | waktu_rata_rata |
+---------+-----------+---------+------------+---------+-----------------+
| Alfa | 00:00:29 | 3 | 00:00:35 | 1 | 31.3333 |
| Beta | 00:00:30 | 3 | 00:00:33 | 1 | 31.3333 |
| Charlie | 00:00:28 | 2 | 00:00:31 | 3 | 29.3333 |
+---------+-----------+---------+------------+---------+-----------------+

Bagaimana querynya?

Langsung saja, berikut ini querynya:

1. SELECT nama,
2. waktu_min,
3. ( SELECT percobaan
4. FROM pelari AS p2
5. WHERE p2.waktu_lari = p1.waktu_min
6. AND p2.nama = p1.nama
7. ) AS perc_ke,
8. waktu_maks,
9. (SELECT percobaan
10. FROM pelari AS p2
11. WHERE p2.waktu_lari = p1.waktu_maks
12. AND p2.nama = p1.nama
13. ) AS perc_ke,
14. waktu_rata_rata

240 BAB 13 Menguasai DATE TIME


15. FROM
16. ( SELECT nama, MIN(waktu_lari) AS waktu_min,
17. MAX(waktu_lari) AS waktu_maks,
18. AVG(waktu_lari) AS waktu_rata_rata
19. FROM pelari AS p1
20. GROUP BY nama
21. ) AS p1

Pada query diatas, pertama kita buat temporary tabel (pada klausa
from) yang kita beri nama p1 dengan bentuk sama seperti pada
contoh sebelumnya:
+---------+---------------+----------------+-----------------+
| nama | waktu_min | waktu_maks | waktu_rata_rata |
+---------+---------------+----------------+-----------------+
| Alfa | 00:00:29 | 00:00:35 | 31.3333 |
| Beta | 00:00:30 | 00:00:33 | 31.3333 |
| Charlie | 00:00:28 | 00:00:31 | 29.3333 |
+---------+---------------+----------------+-----------------+

selanjutnya untuk kolom perc_ke, kita buat subquery dengan kriteria


yang melibatkan tabel p1 dan p2.

Seperti yang telah kita pelajari bahwa ketika mengeksekusi klausa


SELECT, maka setiap baris tabel hasil klausa FROM akan dieksekusi,
begitupun subquery pada klausa SELECT

Killer Trik Query MySQL 241


Halaman ini sengaja dikosongkan
Jagowebdev.com

242 BAB 13 Menguasai DATE TIME


BAB 14 Table Reporting
Pada kondisi tertentu, kita perlu untuk menampilkan data dalam
bentuk laporan. Data laporan ini biasanya berbentuk tabel dengan
susunan tertentu. Data ini bisa dibuat dari sisi aplikasi
(menggunakan bahas Visual Basic, PHP, ASP, dll) maupun sisi
database.

Untuk membuat table report dengan coding pada aplikasi, biasanya


membutuhkan logika yang kompleks, untuk itu sebisa mungkin
diselesaikan di sisi query database.

Kali ini kita akan membahas beberapa contoh bentuk table reporting
dengan query SQL yang dapat Anda gunakan sebagai ide
pemecahan masalah yang Anda hadapi.

14.1. Table Report I


Pada contoh yang pertama ini, kita akan membuat report data
anggota koperasi beserta jumlah simpanannya.

Misal kita memiliki tabel kop_anggota yang berisi data anggota


koperasi dan tabel kop_simpanan yang berisi data simpanan tiap-
tiap anggota. Isi dari kedua tabel tersebut adalah sebagai berikut
kop_anggota
+------------+-----------+--------------+----------+--------+------------+
| id_anggota | nip | nama_anggota | alamat | pokok | tgl_daftar |
+------------+-----------+--------------+----------+--------+------------+
| 1 | 060034588 | Alfa | Jakarta | 100000 | 2017-05-21 |
| 2 | 060032533 | Beta | Surabaya | 100000 | 2017-05-22 |
| 3 | 060044565 | Charlie | Semarang | 100000 | 2017-05-23 |
| 4 | 060021345 | Delta | Solo | 100000 | 2017-05-24 |
+------------+-----------+--------------+----------+--------+------------+

kop_simpanan

Killer Trik Query MySQL 243


+-------------+------------+--------+----------+--------------+
| id_simpanan | id_anggota | wajib | sukarela | tgl_simpanan |
+-------------+------------+--------+----------+--------------+
| 1 | 1 | 150000 | 250000 | 2017-05-21 |
| 2 | 2 | 150000 | 350000 | 2017-05-22 |
| 3 | 3 | 150000 | 300000 | 2017-05-23 |
| 4 | 4 | 150000 | 200000 | 2017-05-24 |
| 5 | 2 | 150000 | 300000 | 2017-06-10 |
| ... | ... | ... | ... | ... |
+-------------+------------+--------+----------+--------------+

Keterangan:

Simpanan pokok hanya dibayar sekali ketika anggota mendaftar,


simpanan wajib wajib dibayar perbulan, dan simpanan sukarela
sifatnya tidak wajib, boleh dibayar boleh tidak.

Selanjutnya kita akan menampilkan data anggota beserta


simpanannya dengan bentuk seperti table di bawah ini:
+---------+--------+---------+----------+---------+
| nama | pokok | wajib | sukarela | total |
+---------+--------+---------+----------+---------+
| Alfa | 100000 | 300000 | 550000 | 950000 |
| Beta | 100000 | 300000 | 650000 | 1050000 |
| Charlie | 100000 | 300000 | 650000 | 1050000 |
| Delta | 100000 | 300000 | 600000 | 1000000 |
| TOTAL | 400000 | 1200000 | 2450000 | 4050000 |
+---------+--------+---------+----------+---------+

Bagaimana query SQL-nya?

Pertama…
Untuk membuat query SQL-nya, pertama kita perlu mendefinisikan
tabel hasil klausa FROM.

Untuk memperoleh tabel tersebut kita perlu menganalisa tabel hasil,


pada tabel hasil, terdapat kolom nama dan simpanan pokok
sehingga kita perlu tabel kop_anggota. Selain itu terdapat kolom

244 BAB 14 Table Reporting


simpanan wajib dan sukarela, kedua kolom tersebut ada d tabel
kop_simpanan, sehingga kita perlu tabel kop_simpanan.

Karena kita perlu dua tabel dan tabel hasil klausa FROM harus
berupa satu tabel, maka kita harus menggabungkan keduanya.

Selanjutnya, kita tentukan cara penggabungan tersebut, dengan join


atau UNION?. Kali ini kita menggunakan join dan kita tempatkan
tabel kop_anggota disebelah kiri.

Kenapa demikian?

Karena kedua tabel saling berhubungan (pada kolom id_anggota)


maka kita gunakan join. Selanjutnya karena kita akan menampilkan
data semua anggota maka kita letakkan tabel kop_anggota disebelah
kiri.

Mari kita tes hasil penggabungan nya. Jalankan query berikut:

1. SELECT *
2. FROM kop_anggota
3. LEFT JOIN kop_simpanan USING(id_anggota)

Hasil:
+------------+--------------+--------+------------+-------------+--------+----------+
| id_anggota | nama_anggota | pokok | tgl_daftar | id_simpanan | wajib | sukarela |
+------------+--------------+--------+------------+-------------+--------+----------+
| 1 | Alfa | 100000 | 2017-05-21 | 1 | 150000 | 250000 |
| 2 | Beta | 100000 | 2017-05-22 | 2 | 150000 | 350000 |
| 3 | Charlie | 100000 | 2017-05-23 | 3 | 150000 | 300000 |
| 4 | Delta | 100000 | 2017-05-24 | 4 | 150000 | 200000 |
| 2 | Beta | 100000 | 2017-05-22 | 5 | 150000 | 300000 |
| 1 | Alfa | 100000 | 2017-05-21 | 6 | 150000 | 300000 |
| 4 | Delta | 100000 | 2017-05-24 | 7 | 150000 | 400000 |
| 3 | Charlie | 100000 | 2017-05-23 | 8 | 150000 | 350000 |
+------------+--------------+--------+------------+-------------+--------+----------+

Karena keterbatasan tempat, beberapa kolom tabel diatas tidak


ditampilkan.

Apakah hasilnya sudah benar?

Killer Trik Query MySQL 245


Yup, sudah benar, semua kolom tabel output sudah ada, kecuali
kolom total. Karena kolom total merupakan kolom baru yang tidak
ada pada kedua tabel, maka kita perlu membuatnya pada klausa
SELECT.

Kedua…
Selanjutnya kita definisikan kolom pada klausa SELECT. Dari tabel
hasil klausa FROM, maka kita perlu menjumlahkan data simpanan
wajib dan simpanan sukarela per anggota, sehingga kita gunakan
fungsi SUM, sedangkan untuk simpanan pokok, kita tidak perlu
menjumlahkannya karena simpanan pokok hanya dibayar sekali.

Query jadinya adalah sebagai berikut:

1. SELECT nama_anggota
2. , pokok
3. , SUM(wajib) AS wajib
4. , SUM(sukarela) AS sukarela
5. , pokok + SUM(wajib) + SUM(sukarela) AS total
6. FROM kop_anggota
7. LEFT JOIN kop_simpanan USING(id_anggota)
8. GROUP BY id_anggota

Hasil:
+---------+--------+---------+----------+---------+
| nama | pokok | wajib | sukarela | total |
+---------+--------+---------+----------+---------+
| Alfa | 100000 | 300000 | 550000 | 950000 |
| Beta | 100000 | 300000 | 650000 | 1050000 |
| Charlie | 100000 | 300000 | 650000 | 1050000 |
| Delta | 100000 | 300000 | 600000 | 1000000 |
+---------+--------+---------+----------+---------+

Pada bab terdahulu pernah kita bahas bahwa untuk kolom non
agregasi (pada contoh diatas adalah kolom pokok)) data yang
diambil adalah data pada baris pertama dan karena data kolom

246 BAB 14 Table Reporting


pokok isinya sama semua, yaitu 100.000, maka hasil yang kita
peroleh sesuai dengan yang kita harapkan

Selanjutnya, bagaimana alur query diatas?

Langsung saja, untuk alur querynya, perhatikan ilustrasi berikut:

Gambar 14.1 Ilustrasi Alur Query

Cara Lain…
Banyak jalan menuju Roma, demikian juga dengan permasalahan
diatas.

Untuk menyelesaikan permasalahan diatas, kita dapat


menggunakan cara lain yaitu menggunakan subquery. Dengan

Killer Trik Query MySQL 247


subquery, kita ubah hubungan tabel kop_anggota dan
kop_simpanan, dari one to many menjadi one to one.

Hubungan awal kedua tabel adalah sebagai berikut:

Gambar 14.2 Hubungan One to Many Antara Tabel kop_anggota dan


kop_simpanan

Dari hubungan diatas terlihat bahwa anggota koperasi pada tabel


kop_anggota dapat memiliki simpanan wajib dan sukarela berulang
kali.

Selanjutnya kita ubah hubungan kedua tabel menjadi one to one.


Kenapa demikian?

Karena kita akan menampilkan data per anggota, sehingga data yang
berkaitan juga harus dibentuk per anggota. Bentuk hubungan one-
to-one nya tampak seperti gambar berikut:

Gambar 14.3 Hubungan One to One Antara Tabel kop_anggota dan


kop_simpanan

Bentuk tabel kedua kita dapatkan dengan menjalankan query pada


tabel kop_simpanan sebagai berikut:

248 BAB 14 Table Reporting


1. SELECT id_anggota
2. , SUM(wajib) AS wajib
3. , SUM(sukarela) AS sukarela
4. FROM kop_simpanan
5. GROUP BY id_anggota

Selanjutnya kita susun query utama dan subquerynya sehingga


menghasilkan query utuh sebagai berikut:

1. SELECT nama_anggota
2. , pokok, wajib, sukarela
3. , pokok + wajib + sukarela AS total
4. FROM kop_anggota
5. LEFT JOIN
6. (
7. SELECT id_anggota
8. , SUM(wajib) AS wajib
9. , SUM(sukarela) AS sukarela
10. FROM kop_simpanan
11. GROUP BY id_anggota
12. ) AS simpanan USING (id_anggota)

Hasil:
+---------+--------+--------+----------+---------+
| nama | pokok | wajib | sukarela | total |
+---------+--------+--------+----------+---------+
| Alfa | 100000 | 300000 | 550000 | 950000 |
| Beta | 100000 | 300000 | 650000 | 1050000 |
| Charlie | 100000 | 300000 | 650000 | 1050000 |
| Delta | 100000 | 300000 | 600000 | 1000000 |
+---------+--------+--------+----------+---------+

Perhatikan bahwa pada query utama kita dapat menggunakan


kolom alias (wajib dan sukarela) karena kolom tersebut sudah
terbentuk sempurna pada klausa FROM (subquery).

Membuat Baris Total…


Terakhir, mari kita tambahkan baris total, sehingga bentuk tabel
outputnya sesuai dengan yang kita inginkan di awal.

Killer Trik Query MySQL 249


Untuk membuat baris total, kali ini kita gunakan UNION ALL dengan
membuat tabel baru yang berisi baris total.

Tabel tersebut kita buat dengan terlebih dahulu membuat


hubungan kedua tabel menjadi one-to-one seperti yang kita lakukan
pada cara alternatif pada query sebelumnya

Query tabel total adalah sebagai berikut:

1. SELECT 'TOTAL', pokok, wajib, sukarela


2. , pokok + wajib + sukarela AS total
3. FROM
4. (
5. SELECT id_anggota, SUM(pokok) AS pokok
6. FROM kop_anggota
7. ) AS anggota
8. LEFT JOIN
9. (
10. SELECT id_anggota
11. , SUM(wajib) AS wajib
12. , SUM(sukarela) AS sukarela
13. FROM kop_simpanan
14. ) AS simpanan USING (id_anggota)

Hasil yang kita peroleh:


+-------+--------+---------+----------+---------+
| TOTAL | pokok | wajib | sukarela | total |
+-------+--------+---------+----------+---------+
| TOTAL | 400000 | 1200000 | 2450000 | 4050000 |
+-------+--------+---------+----------+---------+

Masing masing subquery, baik pada klausa FROM maupun klausa


LEFT JOIN akan membentuk tabel total, dengan cara ini akan
mempermudah alur logika kita dalam menggabungkan tabel.

250 BAB 14 Table Reporting


Hubungan kedua tabel hasil subquery diatas tampak seperti gambar
berikut:

Gambar 14.4. Ilustrasi Alur Query

Selanjutnya, dengan left join kita gabungkan kedua tabel tersebut


dan dengan klausa SELECT kita hasilkan tabel total.

Menggabungkan Dengan Baris Total…


Selanjutnya mari kita gabungkan query pada tabel pertama dengan
query tabel total, query jadinya adalah sebagai berikut:

1. SELECT nama_anggota, pokok, wajib, sukarela


2. , pokok + wajib + sukarela AS total
3. FROM kop_anggota
4. LEFT JOIN
5. (
6. SELECT id_anggota, SUM(wajib) AS wajib
7. , SUM(sukarela) AS sukarela

Killer Trik Query MySQL 251


8. FROM kop_simpanan
9. GROUP BY id_anggota
10. ) AS simpanan USING (id_anggota)
11. UNION ALL
12. SELECT 'TOTAL', SUM(pokok), wajib, sukarela
13. , SUM(pokok) + wajib + sukarela AS total
14. FROM kop_anggota
15. LEFT JOIN
16. (
17. SELECT id_anggota, SUM(wajib) AS wajib
18. , SUM(sukarela) AS sukarela
19. FROM kop_simpanan
20. ) AS simpanan USING (id_anggota)

Hasil:
+--------------+--------+---------+----------+---------+
| nama_anggota | pokok | wajib | sukarela | total |
+--------------+--------+---------+----------+---------+
| Alfa | 100000 | 300000 | 550000 | 950000 |
| Beta | 100000 | 300000 | 650000 | 1050000 |
| Charlie | 100000 | 300000 | 650000 | 1050000 |
| Delta | 100000 | 300000 | 600000 | 1000000 |
| TOTAL | 400000 | 1200000 | 2450000 | 4050000 |
+--------------+--------+---------+----------+---------+

Pada query diatas, query pertama yang kita gunakan adalah cara
alternatif (one to one relationship), karena lebih aman, dimana
simpanan pokoknya hanya ditampilkan sekali.

14.2. Table Report II


Pada contoh kasus table report ke-dua ini, kita akan membuat tabel
laporan target penerimaan pajak daerah dengan realisasi per
triwulan-nya. Ada tiga tabel yang kita gunakan yaitu tabel
pjk_rekening, pjk_target, dan pjk_realisasi

Isi dari ketiga tabel tersebut adalah sebagai berikut:

252 BAB 14 Table Reporting


pjk_rekening
+-------------+----------------+
| id_rekening | nama_rekening |
+-------------+----------------+
| 1 | Pajak Hotel |
| 2 | Pajak Restoran |
| 3 | Pajak Hiburan |
| 4 | Pajak Reklame |
| 5 | Pajak Parkir |
+-------------+----------------+

pjk_target
+----+-------------+-------+----------+------------+
| id | id_rekening | tahun | triwulan | target |
+----+-------------+-------+----------+------------+
| 1 | 1 | 2017 | 1 | 3345000000 |
| 2 | 1 | 2017 | 2 | 3150000000 |
| 3 | 1 | 2017 | 3 | 2725000000 |
| 4 | 1 | 2017 | 4 | 4074000000 |
| 5 | 2 | 2017 | 1 | 4250000000 |
| 6 | 2 | 2017 | 2 | 4725000000 |
| 7 | 2 | 2017 | 3 | 5304000000 |
| 8 | 2 | 2017 | 4 | 6432000000 |
| 9 | 3 | 2017 | 1 | 1125000000 |
| 10 | 3 | 2017 | 2 | 921200000 |
| .. | ... | ... | ... | ... |
+----+-------------+-------+----------+------------+

pjk_realisasi
+----+-------------+------------+-----------+
| id | id_rekening | tanggal | pemasukan |
+----+-------------+------------+-----------+
| 1 | 1 | 2017-01-01 | 30858422 |
| 2 | 1 | 2017-01-02 | 22472129 |
| 3 | 1 | 2017-01-03 | 23665179 |
| 4 | 1 | 2017-01-04 | 31702866 |
| 5 | 1 | 2017-01-05 | 28370283 |
| .. | ... | ... | ... |
+----+-------------+------------+-----------+

Keterangan:

 Tabel pjk_rekening berisi nama jenis pajak

 Tabel pjk_target terdiri target tahun 2017 dan tahun 2016 yang
dibagi per triwulan (1 s.d 4)

Killer Trik Query MySQL 253


 Tabel pjk_realisasi berisi pemasukan harian tahun 2017 dan
2016 untuk masing masing jenis pajak

Selanjutnya kita buat laporan realisasi masing masing jenis pajak per
triwulan khusus untuk tahun 2017. Output yang akan kita buat
adalah sebagai berikut:
+-------------+----------------+-------------+-------------+-------------+----------+
| id_rekening | nama_rekening | target | realisasi | sisa_target | persen |
+-------------+----------------+-------------+-------------+-------------+----------+
| 1 | Pajak Hotel | 13294000000 | 13414756255 | 0 | 100.9084 |
| 2 | Pajak Restoran | 20711000000 | 20803995651 | 0 | 100.4490 |
| 3 | Pajak Hiburan | 4619200000 | 5218201170 | 0 | 112.9676 |
| 4 | Pajak Reklame | 12156000000 | 12512550544 | 0 | 102.9331 |
| 5 | Pajak Parkir | 3040900000 | 3068045855 | 0 | 100.8927 |
| | TOTAL | 53821100000 | 55017549475 | 0 | 102.2230 |
+-------------+----------------+-------------+-------------+-------------+----------+
Lanjutan...
+-------------+---------------+-----------------+------------+
| target_tw1 | realisasi_tw1 | sisa_target_tw1 | persen_tw1 |
+-------------+---------------+-----------------+------------+
| 3345000000 | 2486377516 | 858622484 | 74.33 |
| 4250000000 | 3371797490 | 878202510 | 79.34 |
| 1125000000 | 428999622 | 696000378 | 38.13 |
| 2535000000 | 1735409153 | 799590847 | 68.46 |
| 725900000 | 638265925 | 87634075 | 87.93 |
| 11980900000 | 8660849706 | 3320050294 | 72.29 |
+-------------+---------------+-----------------+------------+
dst… s.d triwulan 4

Bagaimana querynya?

Pertama…
Pertama kita identifikasi tabel output untuk membuat bentuk tabel
pada klausa FROM. Pada tabel tersebut terdapat kolom nama
rekening, target, dan realisasi, sehingga kita perlu menghubungkan
ketiga tabel, bagaimana model penggabungannya?

Kali ini, kita gunakan join dan kita tempatkan tabel pjk_rekening di
paling kiri (karena kita akan menampilkan semua nama rekening).

Untuk bentuk tabel pjk_target dan pjk_realisasi, karena kita akan


menampilkan data per rekening, maka kita perlu mengelompokkan

254 BAB 14 Table Reporting


data per id rekening, selain itu karena terdapat kolom triwulan baik
untuk target dan realisasi, maka kita juga perlu membuat kolom
triwulan pada kedua tabel tersebut.

Bentuk kedua tabel kita ubah menjadi seperti berikut ini:

Tabel pjk_target:
+-------------+-------------+------------+------------+------------+------------+
| id_rekening | target | target_tw1 | target_tw2 | target_tw3 | target_tw4 |
+-------------+-------------+------------+------------+------------+------------+
| 1 | 13294000000 | 3345000000 | 3150000000 | 2725000000 | 4074000000 |
| 2 | 20711000000 | 4250000000 | 4725000000 | 5304000000 | 6432000000 |
| 3 | 4619200000 | 1125000000 | 921200000 | 1250000000 | 1323000000 |
| 4 | 12156000000 | 2535000000 | 2243000000 | 3653000000 | 3725000000 |
| 5 | 3040900000 | 725900000 | 696400000 | 753200000 | 865400000 |
+-------------+-------------+------------+------------+------------+------------+

Query yang diperlukan:

1. SELECT id_rekening,
2. SUM( target ) AS target,
3. SUM(IF (triwulan = 1, target, 0)) AS target_tw1,
4. SUM(IF (triwulan = 2, target, 0)) AS target_tw2,
5. SUM(IF (triwulan = 3, target, 0)) AS target_tw3,
6. SUM(IF (triwulan = 4, target, 0)) AS target_tw4
7. FROM pjk_target
8. WHERE tahun = 2017
9. GROUP BY id_rekening

Tabel pjk_realisasi:
+-------------+-------------+---------------+---------------+---------------+---------------+
| id_rekening | realisasi | realisasi_tw1 | realisasi_tw2 | realisasi_tw3 | realisasi_tw4 |
+-------------+-------------+---------------+---------------+---------------+---------------+
| 1 | 13414756255 | 2486377516 | 2530160543 | 3256605372 | 5141612824 |
| 2 | 20803995651 | 3371797490 | 4138996875 | 5809459112 | 7483742174 |
| 3 | 5218201170 | 428999622 | 533177637 | 1790684580 | 2465339331 |
| 4 | 12512550544 | 1735409153 | 1669410671 | 4212431261 | 4895299459 |
| 5 | 3068045855 | 638265925 | 634578723 | 811158996 | 984042211 |
+-------------+-------------+---------------+---------------+---------------+---------------+

Query yang diperlukan:

Killer Trik Query MySQL 255


1. SELECT id_rekening,
2. SUM( IF (YEAR(tanggal) = 2017, pemasukan, 0)
3. ) AS realisasi,
4. SUM(IF(tanggal >= '2017-01-01'
5. AND tanggal <= '2017-03-31', pemasukan, 0)
6. ) AS realisasi_tw1,
7. SUM(IF(tanggal >= '2017-04-01'
8. AND tanggal <= '2017-06-31', pemasukan, 0)
9. ) AS realisasi_tw2,
10. SUM(IF(tanggal >= '2017-07-01'
11. AND tanggal <= '2017-09-31', pemasukan, 0)
12. ) AS realisasi_tw3,
13. SUM(IF(tanggal >= '2017-10-01'
14. AND tanggal <= '2017-12-31', pemasukan, 0)
15. ) AS realisasi_tw4
16. FROM pjk_realisasi
17. GROUP BY id_rekening

Bagaimana alur query diatas? Alur querynya sama seperti


penjelasan pada kasus sebelum sebelumnya yang menggunakan
fungsi agregasi dan IF.

Jika kita gabungkan, maka klausa FROM akan berbentuk:

1. SELECT ...
2. FROM pjk_rekening
3. LEFT JOIN
4. (
5. SELECT id_rekening,
6. SUM( target ) AS target,
7. SUM(IF (triwulan = 1, target, 0)) AS target_tw1,
8. SUM(IF (triwulan = 2, target, 0)) AS target_tw2,
9. SUM(IF (triwulan = 3, target, 0)) AS target_tw3,
10. SUM(IF (triwulan = 4, target, 0)) AS target_tw4
11. FROM pjk_target
12. WHERE tahun = 2017
13. GROUP BY id_rekening
14. ) AS target USING(id_rekening)
15. LEFT JOIN
16. (
17. SELECT id_rekening,
18. SUM( IF (YEAR(tanggal) = 2017, pemasukan, 0)
19. ) AS realisasi,
20. SUM(IF(tanggal >= '2017-01-01'

256 BAB 14 Table Reporting


21. AND tanggal <= '2017-03-31', pemasukan, 0)
22. ) AS realisasi_tw1,
23. SUM(IF(tanggal >= '2017-04-01'
24. AND tanggal <= '2017-06-31', pemasukan, 0)
25. ) AS realisasi_tw2,
26. SUM(IF(tanggal >= '2017-07-01'
27. AND tanggal <= '2017-09-31', pemasukan, 0)
28. ) AS realisasi_tw3,
29. SUM(IF(tanggal >= '2017-10-01'
30. AND tanggal <= '2017-12-31', pemasukan, 0)
31. ) AS realisasi_tw4
32. FROM pjk_realisasi
33. GROUP BY id_rekening
34. ) AS realisasi USING (id_rekening)

Kedua…
Selanjutnya setelah kita selesai menyusun bentuk tabel pada klausa
FROM, maka kita perlu menyusun klausa SELECT

Pada tabel output, terdapat kolom sisa target dan persentase


realisasi terhadap target. Sisa target diperoleh dari nilai pada kolom
target – realisasi, sedangkan untuk persentase, diperoleh dari
realisasi / target

Keseluruhan bentuk querynya adalah sebagai berikut:

1. SELECT id_rekening, nama_rekening,


2.
3. target,
4. realisasi,
5. IF (target - realisasi < 0, 0
6. , target - realisasi) AS sisa_target,
7. ROUND(realisasi / target * 100, 4) AS persen,
8.
9. target_tw1,
10. realisasi_tw1,
11. IF (target_tw1 - realisasi_tw1 < 0, 0
12. , target_tw1 - realisasi_tw1) AS sisa_target_tw1,
13. ROUND(realisasi_tw1 / target_tw1 * 100, 2) AS persen_tw1,
14.
15. target_tw2,

Killer Trik Query MySQL 257


16. realisasi_tw2,
17. IF (target_tw2 - realisasi_tw2 < 0, 0
18. , target_tw2 - realisasi_tw2) AS sisa_target_tw2,
19. ROUND(realisasi_tw2 / target_tw2 * 100, 2) AS persen_tw2,
20.
21. target_tw3,
22. realisasi_tw3,
23. IF (target_tw3 - realisasi_tw3 < 0, 0
24. , target_tw3 - realisasi_tw3) AS sisa_target_tw3,
25. ROUND(realisasi_tw3 / target_tw3 * 100, 2) AS persen_tw3,
26.
27. target_tw4,
28. realisasi_tw4,
29. IF (target_tw4 - realisasi_tw4 < 0, 0
30. , target_tw4 - realisasi_tw4) AS sisa_target_tw4,
31. ROUND(realisasi_tw4 / target_tw4 * 100, 2) AS persen_tw4
32.
33. FROM pjk_rekening
34. LEFT JOIN
35. (
36. SELECT id_rekening,
37. SUM( target ) AS target,
38. SUM(IF (triwulan = 1, target, 0)) AS target_tw1,
39. SUM(IF (triwulan = 2, target, 0)) AS target_tw2,
40. SUM(IF (triwulan = 3, target, 0)) AS target_tw3,
41. SUM(IF (triwulan = 4, target, 0)) AS target_tw4
42. FROM pjk_target
43. WHERE tahun = 2017
44. GROUP BY id_rekening
45. ) AS target USING(id_rekening)
46. LEFT JOIN
47. (
48. SELECT id_rekening,
49. SUM( IF (YEAR(tanggal) = 2017, pemasukan, 0)
50. ) AS realisasi,
51. SUM(IF(tanggal >= '2017-01-01'
52. AND tanggal <= '2017-03-31', pemasukan, 0)
53. ) AS realisasi_tw1,
54. SUM(IF(tanggal >= '2017-04-01'
55. AND tanggal <= '2017-06-31', pemasukan, 0)
56. ) AS realisasi_tw2,
57. SUM(IF(tanggal >= '2017-07-01'
58. AND tanggal <= '2017-09-31', pemasukan, 0)
59. ) AS realisasi_tw3,
60. SUM(IF(tanggal >= '2017-10-01'
61. AND tanggal <= '2017-12-31', pemasukan, 0)
62. ) AS realisasi_tw4

258 BAB 14 Table Reporting


63. FROM pjk_realisasi
64. GROUP BY id_rekening
65. ) AS realisasi USING (id_rekening)

Pada klausa select kita menggunakan fungsi IF target - realisasi <


0 untuk menghindari nilai sisa_target negatif, selain itu kita gunakan
fungsi ROUND untuk melakukan pembulatan 2 desimal di belakang
koma.

Hasil dari query diatas adalah:


+-------------+----------------+-------------+-------------+-------------+----------+
| id_rekening | nama_rekening | target | realisasi | sisa_target | persen |
+-------------+----------------+-------------+-------------+-------------+----------+
| 1 | Pajak Hotel | 13294000000 | 13414756255 | 0 | 100.9084 |
| 2 | Pajak Restoran | 20711000000 | 20803995651 | 0 | 100.4490 |
| 3 | Pajak Hiburan | 4619200000 | 5218201170 | 0 | 112.9676 |
| 4 | Pajak Reklame | 12156000000 | 12512550544 | 0 | 102.9331 |
| 5 | Pajak Parkir | 3040900000 | 3068045855 | 0 | 100.8927 |
+-------------+----------------+-------------+-------------+-------------+----------+
Lanjutan...
+-------------+---------------+-----------------+------------+
| target_tw1 | realisasi_tw1 | sisa_target_tw1 | persen_tw1 |
+-------------+---------------+-----------------+------------+
| 3345000000 | 2486377516 | 858622484 | 74.33 |
| 4250000000 | 3371797490 | 878202510 | 79.34 |
| 1125000000 | 428999622 | 696000378 | 38.13 |
| 2535000000 | 1735409153 | 799590847 | 68.46 |
| 725900000 | 638265925 | 87634075 | 87.93 |
+-------------+---------------+-----------------+------------+

Membuat baris total…


Baris total dapat dibuat dengan menggunakan klausa WITH ROLLUP,
namun dengan klausa tersebut, kita harus menggunakan fungsi
SUM() pada setiap kolom pada klausa SELECT yang ingin dibuat baris
totalnya, meskipun kolom tersebut tidak perlu di SUM()

Nah, agar lebih mudah mengaturnya, kita buat tabel total tersendiri,
kemudian kita gabungkan dengan tabel utama. Query untuk
membuat tabel total adalah sebagai berikut:

Killer Trik Query MySQL 259


1. SELECT "", "TOTAL",
2. target,
3. realisasi,
4. IF (target - realisasi < 0, 0
5. , target - realisasi) AS sisa_target,
6. ROUND(realisasi / target * 100, 4) AS persen,
7.
8. target_tw1,
9. realisasi_tw1,
10. IF (target_tw1 - realisasi_tw1 < 0, 0
11. , target_tw1 - realisasi_tw1) AS sisa_target_tw1,
12. ROUND(realisasi_tw1 / target_tw1 * 100, 2) AS persen_tw1,
13.
14. target_tw2,
15. realisasi_tw2,
16. IF (target_tw2 - realisasi_tw2 < 0, 0
17. , target_tw2 - realisasi_tw2) AS sisa_target_tw2,
18. ROUND(realisasi_tw2 / target_tw2 * 100, 2) AS persen_tw2,
19.
20. target_tw3,
21. realisasi_tw3,
22. IF (target_tw3 - realisasi_tw3 < 0, 0
23. , target_tw3 - realisasi_tw3) AS sisa_target_tw3,
24. ROUND(realisasi_tw3 / target_tw3 * 100, 2) AS persen_tw3,
25.
26. target_tw4,
27. realisasi_tw4,
28. IF (target_tw4 - realisasi_tw4 < 0, 0
29. , target_tw4 - realisasi_tw4) AS sisa_target_tw4,
30. ROUND(realisasi_tw4 / target_tw4 * 100, 2) AS persen_tw4
31.
32. FROM
33. (
34. SELECT id_rekening,
35. SUM( target ) AS target,
36. SUM(IF (triwulan = 1, target, 0)) AS target_tw1,
37. SUM(IF (triwulan = 2, target, 0)) AS target_tw2,
38. SUM(IF (triwulan = 3, target, 0)) AS target_tw3,
39. SUM(IF (triwulan = 4, target, 0)) AS target_tw4
40. FROM pjk_target
41. WHERE tahun = 2017
42. ) AS target
43. LEFT JOIN
44. (
45. SELECT id_rekening,
46. SUM(pemasukan) AS realisasi,
47. SUM(IF(tanggal >= '2017-01-01'

260 BAB 14 Table Reporting


48. AND tanggal <= '2017-03-31', pemasukan, 0)
49. ) AS realisasi_tw1,
50. SUM(IF(tanggal >= '2017-04-01'
51. AND tanggal <= '2017-06-31', pemasukan, 0)
52. ) AS realisasi_tw2,
53. SUM(IF(tanggal >= '2017-07-01'
54. AND tanggal <= '2017-09-31', pemasukan, 0)
55. ) AS realisasi_tw3,
56. SUM(IF(tanggal >= '2017-10-01'
57. AND tanggal <= '2017-12-31', pemasukan, 0)
58. ) AS realisasi_tw4
59. FROM pjk_realisasi
60. ) AS realisasi USING (id_rekening)

Query diatas sama persis dengan query sebelumnya (


penggabungan tabel), bedanya tabel pjk_rekening kita hilangkan,
selain itu, GROUP BY pada masing masing query kita hilangkan.

Hasil query diatas adalah:


+--+-------+-------------+-------------+-------------+----------+
| | TOTAL | target | realisasi | sisa_target | persen |
+--+-------+-------------+-------------+-------------+----------+
| | TOTAL | 53821100000 | 55017549475 | 0 | 102.2230 |
+--+-------+-------------+-------------+-------------+----------+
Lanjutan...
+-------------+---------------+-----------------+------------+
| target_tw1 | realisasi_tw1 | sisa_target_tw1 | persen_tw1 |
+-------------+---------------+-----------------+------------+
| 11980900000 | 8660849706 | 3320050294 | 72.29 |
+-------------+---------------+-----------------+------------+
dst... s.d triwulan4

Selanjutnya, dengan UNION ALL, kita gabungkan kedua query diatas


sehinnga menghasilkan tabel sesuai dengan yang kita inginkan.

Latihan:
Sebagai latihan silakan coba untuk membanding pencapaian tahun
2017 dengan 2016 kemudian tampilkan persentase pertumbuhan
penerimaannya.

Killer Trik Query MySQL 261


14.3. Table Report III
Pada table report kali ini kita akan melanjutkan contoh pada bagian
sebelumnya yaitu menampilkan data target pajak dan realisasinya
untuk tahun 2017, namun kali ini layout tabel yang ingin kita buat
adalah sebagai berikut:
+-------------+----------------+-------------+-------------+--------+
| id_rekening | triwulan | target | realisasi | persen |
+-------------+----------------+-------------+-------------+--------+
| 1 | Pajak Hotel | 13294000000 | 13414756255 | 100.91 |
| 1 | Triwulan 1 | 3345000000 | 2486377516 | 74.33 |
| 1 | Triwulan 2 | 3150000000 | 2530160543 | 80.32 |
| 1 | Triwulan 3 | 2725000000 | 3256605372 | 119.51 |
| 1 | Triwulan 4 | 4074000000 | 5141612824 | 126.21 |
| 2 | Pajak Restoran | 20711000000 | 20803995651 | 100.45 |
| 2 | Triwulan 1 | 4250000000 | 3371797490 | 79.34 |
| 2 | Triwulan 2 | 4725000000 | 4138996875 | 87.60 |
| 2 | Triwulan 3 | 5304000000 | 5809459112 | 109.53 |
| 2 | Triwulan 4 | 6432000000 | 7483742174 | 116.35 |
| 3 | Pajak Hiburan | 4619200000 | 5218201170 | 112.97 |
| 3 | Triwulan 1 | 1125000000 | 428999622 | 38.13 |
| 3 | Triwulan 2 | 921200000 | 533177637 | 57.88 |
| 3 | Triwulan 3 | 1250000000 | 1790684580 | 143.25 |
| 3 | Triwulan 4 | 1323000000 | 2465339331 | 186.34 |
| 4 | Pajak Reklame | 12156000000 | 12512550544 | 102.93 |
| 4 | Triwulan 1 | 2535000000 | 1735409153 | 68.46 |
| 4 | Triwulan 2 | 2243000000 | 1669410671 | 74.43 |
| 4 | Triwulan 3 | 3653000000 | 4212431261 | 115.31 |
| 4 | Triwulan 4 | 3725000000 | 4895299459 | 131.42 |
| 5 | Pajak Parkir | 3040900000 | 3068045855 | 100.89 |
| 5 | Triwulan 1 | 725900000 | 638265925 | 87.93 |
| 5 | Triwulan 2 | 696400000 | 634578723 | 91.12 |
| 5 | Triwulan 3 | 753200000 | 811158996 | 107.70 |
| 5 | Triwulan 4 | 865400000 | 984042211 | 113.71 |
+-------------+----------------+-------------+-------------+--------+

Untuk membuat query sehingga menghasilkan layout seperti diatas,


maka kita perlu untuk menganalisa masing masing data pada tabel
output.

Pada tabel diatas, terdapat data total untuk tiap-tiap jenis pajak
disertai dengan rincian pendapatan per triwulannya, untuk itu perlu

262 BAB 14 Table Reporting


kita buat dua buah tabel, yang pertama adalah tabel total perjenis
pajak dan yang kedua adalah total per triwulannya.

Tabel 1: total perjenis pajak:


+-------------+----------------+-------------+-------------+--------+
| id_rekening | nama_rekening | target | realisasi | persen |
+-------------+----------------+-------------+-------------+--------+
| 1 | Pajak Hotel | 13294000000 | 13414756255 | 100.91 |
| 2 | Pajak Restoran | 20711000000 | 20803995651 | 100.45 |
| 3 | Pajak Hiburan | 4619200000 | 5218201170 | 112.97 |
| 4 | Pajak Reklame | 12156000000 | 12512550544 | 102.93 |
| 5 | Pajak Parkir | 3040900000 | 3068045855 | 100.89 |
+-------------+----------------+-------------+-------------+--------+

Tabel 2: Total per triwulan


+-------------+------------+------------+------------+--------+
| id_rekening | triwulan | target | realisasi | persen |
+-------------+------------+------------+------------+--------+
| 1 | Triwulan 1 | 3345000000 | 2486377516 | 74.33 |
| 1 | Triwulan 2 | 3150000000 | 2530160543 | 80.32 |
| 1 | Triwulan 3 | 2725000000 | 3256605372 | 119.51 |
| 1 | Triwulan 4 | 4074000000 | 5141612824 | 126.21 |
| 2 | Triwulan 1 | 4250000000 | 3371797490 | 79.34 |
| 2 | Triwulan 2 | 4725000000 | 4138996875 | 87.60 |
| 2 | Triwulan 3 | 5304000000 | 5809459112 | 109.53 |
| 2 | Triwulan 4 | 6432000000 | 7483742174 | 116.35 |
| 3 | Triwulan 1 | 1125000000 | 428999622 | 38.13 |
| 3 | Triwulan 2 | 921200000 | 533177637 | 57.88 |
| 3 | Triwulan 3 | 1250000000 | 1790684580 | 143.25 |
| 3 | Triwulan 4 | 1323000000 | 2465339331 | 186.34 |
| 4 | Triwulan 1 | 2535000000 | 1735409153 | 68.46 |
| 4 | Triwulan 2 | 2243000000 | 1669410671 | 74.43 |
| 4 | Triwulan 3 | 3653000000 | 4212431261 | 115.31 |
| 4 | Triwulan 4 | 3725000000 | 4895299459 | 131.42 |
| 5 | Triwulan 1 | 725900000 | 638265925 | 87.93 |
| 5 | Triwulan 2 | 696400000 | 634578723 | 91.12 |
| 5 | Triwulan 3 | 753200000 | 811158996 | 107.70 |
| 5 | Triwulan 4 | 865400000 | 984042211 | 113.71 |
+-------------+------------+------------+------------+--------+

Tabel pertama kita peroleh dengan menjalankan query berikut:

1. SELECT id_rekening, nama_rekening, target, realisasi


2. , ROUND (realisasi / target * 100, 2) AS persen

Killer Trik Query MySQL 263


3. FROM pjk_rekening
4. LEFT JOIN
5. ( SELECT id_rekening, SUM(target) AS target
6. FROM pjk_target
7. WHERE tahun = 2017
8. GROUP BY id_rekening
9. ) AS target USING(id_rekening)
10. LEFT JOIN
11. (
12. SELECT id_rekening, SUM(pemasukan) AS realisasi
13. FROM pjk_realisasi
14. WHERE YEAR(tanggal) = 2017
15. GROUP BY id_rekening
16. ) AS realisasi USING (id_rekening)

Query diatas mirip dengan query pada table report II. Sedangkan
query untuk tabel ke 2 adalah sebagai berikut:

1. SELECT id_rekening
2. , CONCAT('Triwulan ', triwulan) AS triwulan
3. , target
4. , realisasi, ROUND(realisasi/target*100, 2) AS persen
5. FROM pjk_rekening
6. LEFT JOIN (
7. SELECT *
8. FROM pjk_target
9. WHERE tahun = 2017
10. ) AS target USING(id_rekening)
11. LEFT JOIN
12. (
13. SELECT id_rekening,
14. CASE WHEN tanggal >= '2017-01-01'
15. AND tanggal <= '2017-03-31'
16. THEN 1
17. WHEN tanggal >= '2017-04-01'
18. AND tanggal <= '2017-06-31'
19. THEN 2
20. WHEN tanggal >= '2017-07-01'
21. AND tanggal <= '2017-09-31'
22. THEN 3
23. WHEN tanggal >= '2017-10-01'
24. AND tanggal <= '2017-12-31'
25. THEN 4
26. END AS triwulan
27. ,SUM(pemasukan) AS realisasi

264 BAB 14 Table Reporting


28. FROM pjk_realisasi
29. GROUP BY id_rekening,
30. CASE WHEN tanggal >= '2017-01-01'
31. AND tanggal <= '2017-03-31'
32. THEN 1
33. WHEN tanggal >= '2017-04-01'
34. AND tanggal <= '2017-06-31'
35. THEN 2
36. WHEN tanggal >= '2017-07-01'
37. AND tanggal <= '2017-09-31'
38. THEN 3
39. WHEN tanggal >= '2017-10-01'
40. AND tanggal <= '2017-12-31'
41. THEN 4
42. END
43. ) AS realisasi USING (id_rekening, triwulan)

Pada query diatas, mungkin Anda asing dengan klausa CASE …


WHEN yang ada pada klausa GROUP BY. Klausa tersebut digunakan
untuk membuat temporary column (tidak terlihat) yang berisi nilai
sesuai dengan kondisi yang ada, misal untuk pemasukan bulan 1 s.d
3 akan bernilai 1, bulan 4 s.d 6 bernilai 2, dst …

Bisa dibayangkan seperti ini:


+----+-------------+------------+-----------+--+
| id | id_rekening | tanggal | pemasukan | |
+----+-------------+------------+-----------+--+
| 1 | 1 | 2016-01-01 | 13982658 | 1|
| 2 | 1 | 2016-01-02 | 14873263 | 1|
| 3 | 1 | 2016-01-03 | 20594174 | 1|
| 4 | 1 | 2016-01-04 | 22998050 | 1|
| 5 | 1 | 2016-01-05 | 14376610 | 1|
| .. | ... | ... | ... |..|
+----+-------------+------------+-----------+--+

Selanjutnya data digabungkan (GROUP BY) per kolom baru tersebut


dan kolom id_rekening.

Killer Trik Query MySQL 265


Terakhir kita gabungkan kedua query diatas dengan UNION ALL dan
kita ubah urutan baris berdasarkan id_rekening dan nama_rekening
sebagai berikut:

1. QUERY 1
2. UNION ALL
3. QUERY 2
4. ORDER BY id_rekening, nama_rekening

Menampilkan data lebih dari satu tahun


Ketika membuat report series data (data berdasarkan waktu), maka
bisa dipastikan data tersebut akan disandingkan antar tahun.

Mengambil contoh sebelumnya, kali ini kita akan membandingkan


data tahun 2017 dan 2016 dengan layout tampilan sebagai berikut:
+-------------+----------------+-------------+----------------+-------------+-------------+
| id_rekening | nama_rekening | target_2017 | realisasi_2017 | persen_2017 | growth_2017 |
+-------------+----------------+-------------+----------------+-------------+-------------+
| 1 | Pajak Hotel | 3345000000 | 13414756255 | 401.04 | 15.57 |
| 1 | Triwulan 1 | 3345000000 | 2486377516 | 74.33 | 33.85 |
| 1 | Triwulan 2 | 3150000000 | 2530160543 | 80.32 | 15.13 |
| 1 | Triwulan 3 | 2725000000 | 3256605372 | 119.51 | 9.97 |
| 1 | Triwulan 4 | 4074000000 | 5141612824 | 126.21 | 12.00 |
| 2 | Pajak Restoran | 4250000000 | 20803995651 | 489.51 | 17.25 |
| 2 | Triwulan 1 | 4250000000 | 3371797490 | 79.34 | 18.70 |
| 2 | Triwulan 2 | 4725000000 | 4138996875 | 87.60 | 29.78 |
| 2 | Triwulan 3 | 5304000000 | 5809459112 | 109.53 | 15.48 |
| 2 | Triwulan 4 | 6432000000 | 7483742174 | 116.35 | 11.99 |
| 3 | Pajak Hiburan | 1125000000 | 5218201170 | 463.84 | 7.88 |
| 3 | Triwulan 1 | 1125000000 | 428999622 | 38.13 | 16.34 |
| 3 | Triwulan 2 | 921200000 | 533177637 | 57.88 | -20.24 |
| 3 | Triwulan 3 | 1250000000 | 1790684580 | 143.25 | 12.23 |
| 3 | Triwulan 4 | 1323000000 | 2465339331 | 186.34 | 11.86 |
| 4 | Pajak Reklame | 2535000000 | 12512550544 | 493.59 | 21.08 |
| 4 | Triwulan 1 | 2535000000 | 1735409153 | 68.46 | 39.46 |
| 4 | Triwulan 2 | 2243000000 | 1669410671 | 74.43 | 29.15 |
| 4 | Triwulan 3 | 3653000000 | 4212431261 | 115.31 | 20.61 |
| 4 | Triwulan 4 | 3725000000 | 4895299459 | 131.42 | 13.72 |
| 5 | Pajak Parkir | 725900000 | 3068045855 | 422.65 | 21.70 |
| 5 | Triwulan 1 | 725900000 | 638265925 | 87.93 | 28.81 |
| 5 | Triwulan 2 | 696400000 | 634578723 | 91.12 | 26.08 |
| 5 | Triwulan 3 | 753200000 | 811158996 | 107.70 | 14.31 |
| 5 | Triwulan 4 | 865400000 | 984042211 | 113.71 | 21.12 |
+-------------+----------------+-------------+----------------+-------------+-------------+
Lanjutan:
+-------------+----------------+-------------+
| target_2016 | realisasi_2016 | persen_2016 |
+-------------+----------------+-------------+
| 2776350000 | 11607475222 | 418.08 |

266 BAB 14 Table Reporting


| 2776350000 | 1857624056 | 66.91 |
| 2772000000 | 2197727477 | 79.28 |
| 2425250000 | 2961335575 | 122.10 |
| 3503640000 | 4590788114 | 131.03 |
| 3655000000 | 17742832868 | 485.44 |
| 3655000000 | 2840576041 | 77.72 |
| 3780000000 | 3189228661 | 84.37 |
| 4455360000 | 5030681762 | 112.91 |
| 5595840000 | 6682346404 | 119.42 |
| 990000000 | 4836850816 | 488.57 |
| 990000000 | 368736385 | 37.25 |
| 727748000 | 668503145 | 91.86 |
| 1012500000 | 1595617261 | 157.59 |
| 1045170000 | 2203994025 | 210.87 |
| 2154750000 | 10334169439 | 479.60 |
| 2154750000 | 1244389532 | 57.75 |
| 1884120000 | 1292588179 | 68.60 |
| 2922400000 | 3492658822 | 119.51 |
| 3315250000 | 4304532906 | 129.84 |
| 580720000 | 2520914715 | 434.10 |
| 580720000 | 495509936 | 85.33 |
| 564084000 | 503328329 | 89.23 |
| 655284000 | 709604775 | 108.29 |
| 718282000 | 812471675 | 113.11 |
+-------------+----------------+-------------+

Meskipun data yang kita tampilkan sedikit berbeda dengan contoh


sebelumnya, query yang kita gunakan memiliki perbedaan yang
cukup signifikan karena kita banyak menggunakan fungsi logika
untuk mendapatkan nilai tahun 2016 dan 2017.

Namun demikian, cara berfikir kita tetap sama yaitu kita buat dua
buah tabel: tabel total dan tabel total per triwulan, kemudian kita
gunakan UNION ALL untuk menggabungkan kedua tabel tersebut.

Tabel pertama…
Tabel pertama kita dapatkan dengan query berikut:

1. SELECT id_rekening, nama_rekening


2. , target_2017, realisasi_2017
3. , ROUND (realisasi_2017 / target_2017 * 100, 2)
4. AS persen_2017
5. , ROUND ((realisasi_2017 - realisasi_2016) /
6. realisasi_2016 * 100, 2) AS growth_2017
7. , target_2016, realisasi_2016
8. , ROUND (realisasi_2016 / target_2016 * 100, 2)
9. AS persen_2016

Killer Trik Query MySQL 267


10. FROM pjk_rekening
11. LEFT JOIN
12. ( SELECT *
13. FROM
14. (
15. SELECT id_rekening, target AS target_2017
16. FROM pjk_target
17. WHERE tahun = 2017
18. GROUP BY id_rekening
19. ) AS target_2017
20. LEFT JOIN
21. (SELECT id_rekening, target AS target_2016
22. FROM pjk_target
23. WHERE tahun = 2016
24. GROUP BY id_rekening
25. ) AS target_2016 USING (id_rekening)
26. ) AS target USING(id_rekening)
27. LEFT JOIN
28. (
29. SELECT id_rekening
30. , SUM(IF(YEAR(tanggal) = 2017, pemasukan, null))
31. AS realisasi_2017
32. , SUM(IF(YEAR(tanggal) = 2016, pemasukan, null))
33. AS realisasi_2016
34. FROM pjk_realisasi
35. GROUP BY id_rekening
36. ) AS realisasi USING (id_rekening)

Hasil yang kita dapatkan:


+-------------+----------------+-------------+----------------+-------------+-------------+
| id_rekening | nama_rekening | target_2017 | realisasi_2017 | persen_2017 | growth_2017 |
+-------------+----------------+-------------+----------------+-------------+-------------+
| 1 | Pajak Hotel | 3345000000 | 13414756255 | 401.04 | 15.57 |
| 2 | Pajak Restoran | 4250000000 | 20803995651 | 489.51 | 17.25 |
| 3 | Pajak Hiburan | 1125000000 | 5218201170 | 463.84 | 7.88 |
| 4 | Pajak Reklame | 2535000000 | 12512550544 | 493.59 | 21.08 |
| 5 | Pajak Parkir | 725900000 | 3068045855 | 422.65 | 21.70 |
+-------------+----------------+-------------+----------------+-------------+-------------+
Lanjutan:
+-------------+----------------+-------------+
| target_2016 | realisasi_2016 | persen_2016 |
+-------------+----------------+-------------+
| 2776350000 | 11607475222 | 418.08 |
| 3655000000 | 17742832868 | 485.44 |
| 990000000 | 4836850816 | 488.57 |
| 2154750000 | 10334169439 | 479.60 |
| 580720000 | 2520914715 | 434.10 |
+-------------+----------------+-------------+

268 BAB 14 Table Reporting


Pada query diatas, terdapat self join yaitu pada tabel pjk_target (baris
12 – 25) sebagai berikut:

1. SELECT *
2. FROM
3. (
4. SELECT id_rekening, target AS target_2017
5. FROM pjk_target
6. WHERE tahun = 2017
7. GROUP BY id_rekening
) AS target_2017
8.
LEFT JOIN
9.
( SELECT id_rekening, target AS target_2016
10. FROM pjk_target
11. WHERE tahun = 2016
12. GROUP BY id_rekening
13. ) AS target_2016 USING (id_rekening)
14.

Ket: Istilah Self Join mengacu pada bentuk join dimana tabel yang
digabungkan adalah tabel yang sama.

Cara ini digunakan untuk menyandingkan target tahun 2017 dan


2016. Jika kita jalankan, query ini akan menghasilkan tabel:
+-------------+-------------+-------------+
| id_rekening | target_2017 | target_2016 |
+-------------+-------------+-------------+
| 1 | 3345000000 | 2776350000 |
| 2 | 4250000000 | 3655000000 |
| 3 | 1125000000 | 990000000 |
| 4 | 2535000000 | 2154750000 |
| 5 | 725900000 | 580720000 |
+-------------+-------------+-------------+

Tabel kedua…
Tabel kedua, yaitu rekap pemasukan per triwulan, kita dapatkan
dengan menjalankan query berikut:

1. SELECT id_rekening
2. , CONCAT('Triwulan ', triwulan) AS triwulan
3. , target_2017, realisasi_2017

Killer Trik Query MySQL 269


4. , ROUND (realisasi_2017/target_2017*100, 2) AS persen_2017
5. , ROUND ( (realisasi_2017 - realisasi_2016)
6. / realisasi_2016 * 100, 2 ) AS growth_2017
7. , target_2016, realisasi_2016
8. , ROUND (realisasi_2016/target_2016*100, 2) AS persen_2016
9. FROM pjk_rekening
10. LEFT JOIN (
11. SELECT *
12. FROM
13. (
14. SELECT id_rekening, triwulan, target AS target_2017
15. FROM pjk_target
16. WHERE tahun = 2017
17. ) AS target_2017
18. LEFT JOIN
19. (SELECT id_rekening, triwulan, target AS target_2016
20. FROM pjk_target
21. WHERE tahun = 2016
22. ) AS target_2016 USING (id_rekening, triwulan)
23. ) AS target USING(id_rekening)
24. LEFT JOIN
25. (
26. SELECT id_rekening,
27. CASE WHEN MONTH(tanggal) >= '01'
28. AND MONTH(tanggal) <= '03'
29. THEN 1
30. WHEN MONTH(tanggal) >= '04'
31. AND MONTH(tanggal) <= '06'
32. THEN 2
33. WHEN MONTH(tanggal) >= '07'
34. AND MONTH(tanggal) <= '09'
35. THEN 3
36. WHEN MONTH(tanggal) >= '10'
37. AND MONTH(tanggal) <= '12'
38. THEN 4
39. END AS triwulan
40. ,SUM(IF(YEAR(tanggal) = 2017, pemasukan, NULL))
41. AS realisasi_2017
42. ,SUM(IF(YEAR(tanggal) = 2016, pemasukan, NULL))
43. AS realisasi_2016
44. FROM pjk_realisasi
45. GROUP BY id_rekening,
46. CASE WHEN MONTH(tanggal) >= '01'
47. AND MONTH(tanggal) <= '03'
48. THEN 1
49. WHEN MONTH(tanggal) >= '04'
50. AND MONTH(tanggal) <= '06'

270 BAB 14 Table Reporting


51. THEN 2
52. WHEN MONTH(tanggal) >= '07'
53. AND MONTH(tanggal) <= '09'
54. THEN 3
55. WHEN MONTH(tanggal) >= '10'
56. AND MONTH(tanggal) <= '12'
57. THEN 4
58. END
59. ) AS realisasi USING (id_rekening, triwulan)

Hasilnya adalah sebagai berikut:


+-------------+------------+-------------+----------------+-------------+-------------+
| id_rekening | triwulan | target_2017 | realisasi_2017 | persen_2017 | growth_2017 |
+-------------+------------+-------------+----------------+-------------+-------------+
| 1 | Triwulan 1 | 3345000000 | 2486377516 | 74.33 | 33.85 |
| 1 | Triwulan 2 | 3150000000 | 2530160543 | 80.32 | 15.13 |
| 1 | Triwulan 3 | 2725000000 | 3256605372 | 119.51 | 9.97 |
| 1 | Triwulan 4 | 4074000000 | 5141612824 | 126.21 | 12.00 |
| 2 | Triwulan 1 | 4250000000 | 3371797490 | 79.34 | 18.70 |
| 2 | Triwulan 2 | 4725000000 | 4138996875 | 87.60 | 29.78 |
| 2 | Triwulan 3 | 5304000000 | 5809459112 | 109.53 | 15.48 |
| 2 | Triwulan 4 | 6432000000 | 7483742174 | 116.35 | 11.99 |
| 3 | Triwulan 1 | 1125000000 | 428999622 | 38.13 | 16.34 |
| 3 | Triwulan 2 | 921200000 | 533177637 | 57.88 | -20.24 |
| 3 | Triwulan 3 | 1250000000 | 1790684580 | 143.25 | 12.23 |
| 3 | Triwulan 4 | 1323000000 | 2465339331 | 186.34 | 11.86 |
| 4 | Triwulan 1 | 2535000000 | 1735409153 | 68.46 | 39.46 |
| 4 | Triwulan 2 | 2243000000 | 1669410671 | 74.43 | 29.15 |
| 4 | Triwulan 3 | 3653000000 | 4212431261 | 115.31 | 20.61 |
| 4 | Triwulan 4 | 3725000000 | 4895299459 | 131.42 | 13.72 |
| 5 | Triwulan 1 | 725900000 | 638265925 | 87.93 | 28.81 |
| 5 | Triwulan 2 | 696400000 | 634578723 | 91.12 | 26.08 |
| 5 | Triwulan 3 | 753200000 | 811158996 | 107.70 | 14.31 |
| 5 | Triwulan 4 | 865400000 | 984042211 | 113.71 | 21.12 |
+-------------+------------+-------------+----------------+-------------+-------------+
Lanjutan
+-------------+----------------+-------------+
| target_2016 | realisasi_2016 | persen_2016 |
+-------------+----------------+-------------+
| 2776350000 | 1857624056 | 66.91 |
| 2772000000 | 2197727477 | 79.28 |
| 2425250000 | 2961335575 | 122.10 |
| 3503640000 | 4590788114 | 131.03 |
| 3655000000 | 2840576041 | 77.72 |
| 3780000000 | 3189228661 | 84.37 |
| 4455360000 | 5030681762 | 112.91 |
| 5595840000 | 6682346404 | 119.42 |
| 990000000 | 368736385 | 37.25 |
| 727748000 | 668503145 | 91.86 |
| 1012500000 | 1595617261 | 157.59 |
| 1045170000 | 2203994025 | 210.87 |
| 2154750000 | 1244389532 | 57.75 |
| 1884120000 | 1292588179 | 68.60 |
| 2922400000 | 3492658822 | 119.51 |
| 3315250000 | 4304532906 | 129.84 |
| 580720000 | 495509936 | 85.33 |

Killer Trik Query MySQL 271


| 564084000 | 503328329 | 89.23 |
| 655284000 | 709604775 | 108.29 |
| 718282000 | 812471675 | 113.11 |
+-------------+----------------+-------------+

Perhatikan bahwa pada query diatas kita menentukan triwulan pada


tabel pjk_realisasi dengan mengetes bulan pada kolom pemasukan
sebagai berikut:

1. CASE WHEN MONTH(tanggal) >= '01'


2. AND MONTH(tanggal) <= '03'
3. THEN 1
4. WHEN MONTH(tanggal) >= '04'
5. AND MONTH(tanggal) <= '06'
6. THEN 2
7. WHEN MONTH(tanggal) >= '07'
8. AND MONTH(tanggal) <= '09'
9. THEN 3
10. WHEN MONTH(tanggal) >= '10'
11. AND MONTH(tanggal) <= '12'
12. THEN 4
13. END AS triwulan

Hal ini berbeda jika dibanding contoh sebelumnya dimana kita


membandingkan tanggal berdasarkan range tertentu WHEN tanggal
>= "2017-04-01" AND tanggal <= "2017-06-31" hal ini kita lakukan
karena pada contoh sebelumnya kita tes tanggal hanya untuk satu
tahun.

14.4. Keterbatasan
Sejauh ini kita telah membahas berbagai model penyajian data
dengan query SQL.

Dalam praktik, tidak semua penyajian data dapat diselesaikan


dengan query SQL, melainkan harus menggunakan bantuan aplikasi.

Kenapa?

272 BAB 14 Table Reporting


Karena seperti yang telah disinggung pada bab awal, SQL hanyalah
bahasa deklaratif seperti CSS dan HTML, tugas utamanya hanya
untuk mengambil data, meskipun sudah disertai "bonus" berbagai
fungsi untuk pengolahan data.

Sebagai tambahan, SQL tidak didesain sepenuhnya untuk keperluan


penyajian data, sehingga memang tidak didesain untuk dapat
menyelesaikan semua masalah terkait penyajian data.

Nah, untuk itu, Anda harus dapat mengkombinasikan antara data


yang dihasilkan oleh query SQL dan pemrograman aplikasi sehingga
dapat menyelesaikan setiap bentuk penyajian data, betapapun
rumitnya bentuk penyajian data.

Pada BAB berikutnya, kita akan membahas bagaimana


mengkombinasikan hasil query MySQL dengan aplikasi PHP
sehingga dapat menyelesaikan berbagai bentuk penyajian data yang
tidak dapat di lakukan dengan SQL

Killer Trik Query MySQL 273


Halaman ini sengaja dikosongkan
Jagowebdev.com

274 BAB 14 Table Reporting


BAB 15 Table Reporting Dengan PHP
dan MySQL
Pada BAB sebelumnya telah disinggung mengenai keterbatasan SQL
dan keharusan kita untuk dapat mengkolaborasikan data hasil query
SQL dengan aplikasi, sehingga dapat menyelesaikan berbagai
masalah penyajian data dari yang sederhana hingga yang paling
rumit sekalipun.

Banyak aplikasi dari berbagai platform yang dapat terhubung


dengan MySQL, salah satunya yang populer adalah PHP. Pada BAB
ini, kita akan membahas berbagai cara menyajikan report table
beserta variasinya yang sering diterapkan dalam praktek.

Jika Anda tidak familiar dengan PHP, Anda dapat mengambil konsep
atau alur logika yang ada pada setiap kasus yang dibahas, sehingga
dapat diterapkan pada aplikasi Anda.

15.1. Query MySQL Dengan PHP


Pada PHP terdapat beberapa cara untuk menampilkan data dari
query MySQL. Secara garis besar cara tersebut dibagi menjadi dua,
yaitu cara prosedural dan objek (class).

Cara Prosedural dilakukan menggunakan fungsi mysqli_xxx(),


sedangkan dengan dengan objek dilakukan dengan menggunakan
class MySQLi dan PDO.

Pada bab ini kita akan menggunakan cara prosedural menggunakan


fungsi yang diawali dengan mysqli.

Untuk menampilkan data dengan mysqli diperlukan 3 tahap, yaitu:

Killer Trik Query MySQL 275


1. Koneksi ke database dengan mysqli_connect()

2. Menjalankan query dengan fungsi mysqli_query()

3. Ambil hasil menggunakan fungsi tertentu seperti


mysqli_fetch_assoc

Misal kita ingin menampilkan data nama barang dan stok barang
yang ada pada tabel barang. SQL yang kita gunakan adalah:

SELECT * FROM barang

Jika kita jalankan pada software database manager, hasil yang kita
peroleh adalah sebagai berikut:
+-----------+-------------+------+-------+
| kd_barang | nama_barang | stok | harga |
+-----------+-------------+------+-------+
| 1 | Mouse | 14 | 76000 |
| 2 | Flashdisk | 15 | 55000 |
| 3 | Mousepad | 17 | 35000 |
| 4 | Keyboard | 12 | 80000 |
| 5 | Kabel VGA | 7 | 45000 |
+-----------+-------------+------+-------+

Dengan PHP, script yang kita perlukan adalah sebagai berikut:

1. <?php
2. // Koneksi
3. $conn = mysqli_connect('localhost', 'root', '',
4. 'tutorial_trik');
5.
6. // Menjalankan query
7. $query = mysqli_query($conn, 'SELECT * FROM barang');
8.
9. // Menampilkan data
10. while($row = mysqli_fetch_assoc($query)) {
11. echo $row['nama_barang'] . ' ' . $row['harga'] .
12. '<br/>';
13. }

276 BAB 15 Table Reporting Dengan PHP dan MySQL


Jika script tersebut dijalankan pada browser, maka hasil yang kita
peroleh:
Mouse 76000
Flashdisk 55000
Mousepad 35000
Keyboard 80000
Kabel VGA 45000

Bagaimana script PHP tersebut diatas bekerja?

Ketika menjalankan mysqli_query (baris 7) maka php menyimpan


output hasil query sehingga siap untuk ditampilkan. Ketika data
diambil menggunakan fungsi mysqli_fetch_assoc atau fungsi lain,
seperti mysqli_fetch_row, maka data akan diubah ke bentuk array
sesuai dengan urutan baris dan kolom pada tabel output hasil query,
perhatikan ilustrasi berikut:

Killer Trik Query MySQL 277


Gambar 15.1 Ilustrasi Tabel Hasil Query

Selanjutnya, dengan loop while kita ambil data tersebut satu persatu
urut mulai dari atas, hingga terakhir.

Catatan Penting: setelah data di fetch (diambil – pada contoh


diatas menggunakan fungsi mysqli_fetch_assoc) maka hasil
query akan kosong (pada contoh diatas variabel $query),
dengan demikian, variabel tersebut tidak dapat di fetch untuk
kedua kalinya.

278 BAB 15 Table Reporting Dengan PHP dan MySQL


15.2. Table Reporting I
Pada contoh pertama ini, kita akan menampilkan data penjualan dari
masing masing petugas sales per wilayah per bulan.

Pada aplikasi, terdapat opsi untuk memilih wilayah, bulan, dan tahun
penjualan. Opsi ini digunakan untuk memfilter data yang akan
ditampilkan.

Tampilan form yang kita gunakan adalah sebagai berikut:

Gambar 15.2 Tampilan Form Submit

Selanjutnya, ketika form disubmit, maka akan muncul tabel jumlah


penjualan beserta nilai penjualan per bulan untuk masing masing
petugas penjualan yang dikelompokkan per area. Adapun bentuk
tampilan tabel nya adalah sebagai berikut:

Killer Trik Query MySQL 279


Gambar 15.3 Tampilan Tabel Setelah Form di Submit

Tabel Database…
Tabel database yang kita gunakan hanya satu, yaitu tabel sales
dengan data sebagai berikut:
+--------+---------+------------+----------+-----------+
| id_trx | nama | tgl_trx | area | nilai_trx |
+--------+---------+------------+----------+-----------+
| 1 | Alfa | 2017-01-10 | Jakarta | 250000 |
| 2 | Charlie | 2017-01-02 | Medan | 175000 |
| 3 | Bravo | 2017-01-01 | Jakarta | 310000 |
| 4 | Bravo | 2017-02-04 | Jakarta | 250000 |
| 5 | Alfa | 2017-01-15 | Jakarta | 125000 |
| ... | ... | ... | ... | ... |
+--------+---------+------------+----------+-----------+

Model tabel diatas tentu tidak ideal dimana untuk nama dan area
seharusnya dibuat tabel tersendiri kemudian berelasi dengan tabel

280 BAB 15 Table Reporting Dengan PHP dan MySQL


sales, namun untuk mempermudah pembelajaran, kita gunakan
hanya satu tabel.

Kode Form HTML


Script PHP untuk membuat form HTML adalah sebagai berikut:

1. $year_options = array(2017 => 2017, 2016 => 2016);


2. $month_options = array(1=>'Januari'
3. ,'Februari'
4. ,'Maret'
5. ,'April'
6. ,'Mei'
7. ,'Juni'
8. ,'Juli'
9. ,'Agustus'
10. ,'Septembe'
11. ,'Oktober'
12. ,'November'
13. ,'Desember'
14. );
15.
16. <form action="" method="get">
17. <div class="row">
18. <label>Area</label>
19. <div class="col-wrap">
20. <?php $area =
21. array('Jakarta','Medan','Surabaya','Semarang','Surakarta');?>
22. <select name="tahun">
23. <?php
24. foreach ($area as $val) {
25. $selected = @$_GET['area'] == $val ? '
26. selected="selected"' : '';
27. echo '<option value="' . $val . '"' .
28. $selected . '>' . $val . '</option>';
29. }?>
30. </select>
31. </div>
32. </div>
33. <div class="row">
34. <label>Periode</label>
35. <div class="col-wrap">
36. <select name="bulan">
37. <option value="semua">Semua</option>
38. <?php

Killer Trik Query MySQL 281


39. foreach ($bulan as $key => $val) {
40. $selected = @$_GET['bulan'] == $key ?
41. ' selected="selected"' : '';
42. echo '<option value="' . $key . '"' .
43. $selected . '>' . $val . '</option>';
44. }?>
45. </select>
46. <select name="tahun">
47. <?php
48. foreach ($year_options as $key => $val) {
49. $selected = @$_GET['area'] == $key ? '
50. selected="selected"' : '';
51. echo '<option value="' . $key . '"' .
52. $selected . '>' . $val . '</option>';
53. }?>
54. </select>
55. </div>
56. </div>
57. <div class="row">
58. <input class="offset button" type="submit"
59. name="submit" value="Submit"/>
60. </div>
61. </form>

Pada script diatas, kita membuat opsi bulan dan tahun dari array.
Hal ini akan memudahkan kita untuk membuat dan memperbaiki
kode, salah satunya memberi atribut selected="selected" pada opsi
yang dipilih ketika form disubmit

Script PHP diatas jika dijalankan akan menghasilkan kode HTML


sebagai berikut:

1. <form action="" method="get">


2. <div class="row">
3. <label>Area</label>
4. <div class="col-wrap">
5. <select name="tahun">
6. <option value="Jakarta">Jakarta</option>
7. <option value="Medan">Medan</option>
8. <option value="Surabaya">Surabaya</option>
9. <option value="Semarang">Semarang</option>
10. <option value="Surakarta">Surakarta</option>
11. </select>

282 BAB 15 Table Reporting Dengan PHP dan MySQL


12. </div>
13. </div>
14. <div class="row">
15. <label>Periode</label>
16. <div class="col-wrap">
17. <select name="bulan">
18. <option value="semua">Semua</option>
19. <option value="1">Januari</option>
20. <option value="2">Februari</option>
21. <option value="3">Maret</option>
22. <option value="4">April</option>
23. <option value="5">Mei</option>
24. <option value="6">Juni</option>
25. <option value="7">Juli</option>
26. <option value="8">Agustus</option>
27. <option value="9">September</option>
28. <option value="10">Oktober</option>
29. <option value="11">November</option>
30. <option value="12">Desember</option>
31. </select>
32. <select name="tahun">
33. <option value="2017">2017</option>
34. <option value="2016">2016</option>
35. </select></div>
36. </div>
37. <div class="row">
38. <input class="offset button" type="submit"
39. name="submit" value="Submit" />
40. </div>
41. </form>

SQL Query …
Selanjutnya ketika form disubmit (tombol submit di klik), kita jalan
query MySQL untuk mengambil data dari database sesuai dengan
opsi yang dipilih.

Adapun SQL query yang kita gunakan adalah sebagai berikut:

1. SELECT nama,
2. area,
3. COUNT(nilai_trx) AS jml_trx,
4. SUM(nilai_trx) AS total_trx

Killer Trik Query MySQL 283


5. FROM sales
6. GROUP BY sales.area, nama
7. WHERE YEAR(tgl_trx) = 2017
8.

Jika digabung dengan Kode PHP maka bentuk querynya menjadi


sebagai berikut:

1. if (isset($_GET['submit']))
2. {
3. // WHERE
4. $where_area = $_GET['area'] != 'Semua' ? ' AND area =
5. "' . $_GET['area'] . '"' : '';
6. $where_bulan = $_GET['bulan'] != 'semua' ? ' AND
7. MONTH(tgl_trx) = ' . $_GET['bulan'] : '';
8.
9. // QUERY
10. $sql = '
11. SELECT
12. nama,
13. area,
14. COUNT(nilai_trx) AS jml_trx,
15. SUM(nilai_trx) AS total_trx
16. FROM sales
17. WHERE YEAR(tgl_trx) = ' . $_GET['tahun'] . $where_bulan .
18. $where_area
19. . ' GROUP BY sales.area, nama';
20.
21. $query = mysqli_query($conn, $sql);
22.
23. // RESULT
24. if (!$query) {
25. $error[] = mysqli_error($conn) . '<br/><strong>SQL
26. Query</strong>: ' . $sql;
27. } else {
28. $num_rows = mysqli_num_rows($query);
29. if (!$num_rows) {
30. $warning[] = 'Data tidak ditemukan';
31. } else {
32. $hasil_query = true;
33. while($row = mysqli_fetch_array($query)) {
34. $result[$row['area']][] = $row;
35. }
36. }
37. }

284 BAB 15 Table Reporting Dengan PHP dan MySQL


38. }

Pada script diatas, pada bagian Query, bagian WHERE kita


tambahkan script WHERE YEAR(tgl_trx) = ' . $_GET['tahun'] .
$where_bulan . $where_area;, script tersebut akan membentuk
klausa where dengan bentuk sesuai dengan opsi yang dipilih user,
misal jika dipilih area Jakarta, periode Januari, dan tahun 2017, maka
klausa WHERE yang terbentuk adalah: WHERE YEAR(tgl_trx) =
2017 AND MONTH(tgl_trx) = 3 AND area = "Jakarta"

Query pada script diatas akan menghasilkan tabel output sebagai


berikut:
+---------+----------+---------+-----------+
| nama | area | jml_trx | total_trx |
+---------+----------+---------+-----------+
| Alfa | Jakarta | 7 | 1565000 |
| Bravo | Jakarta | 6 | 1660000 |
| Charlie | Medan | 6 | 1875000 |
| Delta | Medan | 3 | 1370000 |
| Erdi | Surabaya | 8 | 1751000 |
| Fera | Surabaya | 7 | 1396000 |
| Geri | Surabaya | 4 | 1168000 |
+---------+----------+---------+-----------+

Mungkin Anda bertanya tanya, dimana letak baris sub totalnya?


Bukankah pada tabel output yang kita inginkan terdapat baris total?

Baris subtotalnya akan kita buat dengan script PHP. Tapi, bukankah
bisa menggunakan query sql?

Ya, dengan menambahkan klausa WITH ROLLUP dengan mudah bisa


kita tambahkan baris subtotal, misal:

1. SELECT nama,
2. area,
3. COUNT(nilai_trx) AS jml_trx,
4. SUM(nilai_trx) AS total_trx

Killer Trik Query MySQL 285


5. FROM sales
6. WHERE YEAR(tgl_trx) = 2017
7. GROUP BY sales.area, nama
8. WITH ROLLUP

Ya, kita bisa menggunakan query diatas, namun dengan query


tersebut, kita akan kesulitan membuat layout tabel seperti yang kita
inginkan (kita bahas pada bagian berikutnya: menampilkan data)
disamping itu, pada bab WITH ROLLUP, telah dibahas berbagai
kelemahan penggunaan metode ini, salah satunya sangat
bergantung pada kolom pada klausa GROUP BY.

Menampilkan Data
Bentuk tampilan data yang akan kita buat sedikit berbeda dengan
tabel output hasil query, dimana baris area ditampilkan tersendiri.

Script untuk menampilkan data adalah sebagai berikut:

1. if (isset($_GET['submit'])) {
2. if ($hasil_query)
3. {
4. $thead = '<th>No</th>
5. <th>Nama</th>
6. <th>Jml Unit</th>
7. <th>Total Nilai</th>';
8.
9. echo '
10. <div class="table-responsive">
11. <table>
12. <thead>' . $thead . '</thead>
13. <tbody>';
14.
15. while($row = mysqli_fetch_array($query)) {
16. $result[$row['area']][] = $row;
17. }
18.
19. $no = 1;
20. foreach($result as $area => $arr)
21. {

286 BAB 15 Table Reporting Dengan PHP dan MySQL


22. echo '<tr class="row-cat">
23. <td>' . $no . '</td>
24. <td colspan="3" class="green">Area: ' .
25. $area . '</td>
26. </tr>';
27.
28. $subtotal['jml_trx'] = $subtotal['total_trx'] = 0;
29.
30. foreach ($arr as $val) {
31. echo '<tr>
32. <td></td>
33. <td>' . $val['nama'] . '</td>
34. <td class="right">' . $val['jml_trx']
35. . '</td>
36. <td class="right">' .
37. number_format($val['total_trx'],0,',','.') . '</td>';
38.
39. $subtotal['jml_trx'] += $val['jml_trx'];
40. $subtotal['total_trx'] += $val['total_trx'];
41. }
42.
43. echo '<tr>
44. <td></td>
45. <td>Sub Total</td>
46. <td class="red
47. right">'.$subtotal['jml_trx'].'</td>
48. <td class="red
49. right">'.$subtotal['total_trx'].'</td>';
50. $no++;
51. }
52.
53. echo '</tbody><tfoot>' . $thead . '</tfoot>
54. </table>
55. </div>';
56. }
57. }
58.

Pada query diatas, pertama tama hasil query ubah bentuknya


menjadi array dengan index nama area dan kita simpan hasilnya ke
variabel $result (baris 15 s.d 17). Hal ini untuk mempermudah kita
untuk menampilkan data.

Bentuk array pada variabel result adalah sebagai berikut:

Killer Trik Query MySQL 287


Array
(
[Jakarta] => Array
(
[0] => Array
(
[0] => Alfa
[nama] => Alfa
[1] => Jakarta
[area] => Jakarta
[2] => 7
[jml_trx] => 7
[3] => 1565000
[total_trx] => 1565000
)

[1] => Array


(
[0] => Bravo
[nama] => Bravo
[1] => Jakarta
[area] => Jakarta
[2] => 6
[jml_trx] => 6
[3] => 1660000
[total_trx] => 1660000
)

)
[Medan] => Array
(
[0] => Array
(
[0] => Charlie
[nama] => Charlie
[1] => Medan
[area] => Medan
[2] => 6
[jml_trx] => 6
[3] => 1875000
[total_trx] => 1875000
)

[1] => Array


(
[0] => Delta
[nama] => Delta
[1] => Medan

288 BAB 15 Table Reporting Dengan PHP dan MySQL


[area] => Medan
[2] => 3
[jml_trx] => 3
[3] => 1370000
[total_trx] => 1370000
)
)

Pada array diatas, terlihat bahwa data array dikelompokkan per area
(index berupa nama area) sehingga akan memudahkan ketika
menampilkan data per area.

Pada script php untuk menampilkan data bagian script untuk


menampilkan data per wilayah dimulai dari baris 20 s.d 52
foreach($result as $area => $arr) { ... }

Lanjutan…
Selanjutnya, coba Anda tambahkan opsi untuk mengubah urutan
data, dimana user dapat memilih data diurutkan berdasarkan total
transaksi, per area, dan nama area, baik secara descending maupun
ascending.

Tampillan formnya adalah sebagai berikut:

Gambar 15.4. Tampilan Form Submit

Killer Trik Query MySQL 289


Silakan dicoba ya…

Sudah?

Baiklah, mari kita bahas…

Pada bentuk ini, kita akan mengurutkan data dalam satu kolom lebih
dari satu arah, perhatikan ilustrasi berikut:

Gambar 15.5 Ilustrasi Urutan Data Pada Tabel Hasil

Model seperti ini tidak dapat dilakukan dengan SQL, karena SQL
hanya dapat mengurutkan satu arah, ascending semua atau
descending semua.

Lalu bagaimana solusinya?

290 BAB 15 Table Reporting Dengan PHP dan MySQL


Solusinya kita buat dua buah query, satu untuk rekap sobtotal dan
satu lagi untuk rekap per nama.

Query untuk rekap subtotal adalah sebagai berikut:

1. SELECT
2. area,
3. COUNT(nilai_trx) AS jml_trx,
4. SUM(nilai_trx) AS total_trx
5. FROM sales
6. WHERE YEAR(tgl_trx) = 2017
7. GROUP BY area

Jika di konversi pada PHP, script nya menjadi:

1. $where_area = $_GET['area'] != 'Semua'


2. ? ' AND area = "' . $_GET['area'] . '"'
3. : '';
4.
5. $where_bulan = $_GET['bulan'] != 'semua'
6. ? ' AND MONTH(tgl_trx) = '.$_GET['bulan']
7. : '';
8.
9. $sql_subtotal =
10. 'SELECT
11. area,
12. COUNT(nilai_trx) AS jml_trx,
13. SUM(nilai_trx) AS total_trx
14. FROM sales
15. WHERE YEAR(tgl_trx) = ' . $_GET['tahun'] . $where_bulan .
16. $where_area. '
17. GROUP BY area
18. ORDER BY ' . $_GET['order'] . ' ' . $_GET['sort'];

Query diatas akan menghasilkan tabel output sebagai berikut:


+----------+---------+-----------+
| area | jml_trx | total_trx |
+----------+---------+-----------+
| Jakarta | 13 | 3225000 |
| Medan | 9 | 3245000 |
| Surabaya | 19 | 4315000 |
+----------+---------+-----------+

Killer Trik Query MySQL 291


Sedangkan untuk rekap data berdasarkan nama, sama persis seperti
query pada klasus sebelumnya, yaitu:

1. SELECT nama,
2. area,
3. COUNT(nilai_trx) AS jml_trx,
4. SUM(nilai_trx) AS total_trx
5. FROM sales
6. WHERE YEAR(tgl_trx) = 2017
7. GROUP BY sales.area, nama

Query diatas jika di ubah ke script PHP menjadi:

1. $sql_nama = '
2. SELECT
3. nama,
4. area,
5. COUNT(nilai_trx) AS jml_trx,
6. SUM(nilai_trx) AS total_trx
7. FROM sales
8. WHERE YEAR(tgl_trx) = ' . $_GET['tahun'] . $where_bulan .
9. $where_area. '
10. GROUP BY sales.area, nama';

Query diatas jika dijalankan akan menghasilkan tabel seperti berikut


ini:
+---------+----------+---------+-----------+
| nama | area | jml_trx | total_trx |
+---------+----------+---------+-----------+
| Alfa | Jakarta | 7 | 1565000 |
| Bravo | Jakarta | 6 | 1660000 |
| Charlie | Medan | 6 | 1875000 |
| Delta | Medan | 3 | 1370000 |
| Erdi | Surabaya | 8 | 1751000 |
| Fera | Surabaya | 7 | 1396000 |
| Geri | Surabaya | 4 | 1168000 |
+---------+----------+---------+-----------+

Kedua tabel akan dihubungkan berdasarkan area, pembahasan


mengenai penggabungan tersebut akan dibahas pada bagian
berikutnya (menampilkan data)

292 BAB 15 Table Reporting Dengan PHP dan MySQL


Menampilkan data
Selanjutnya kita buat script untuk menampilkan data. Untuk
menggabungkan kedua data hasil query kita gunakan index area.
Pada query pertama, pada PHP, bentuk hasil query subtotalnya
adalah sebagai berikut:
Array
(
[0] => Array
(
[area] => Surabaya
[jml_trx] => 19
[total_trx] => 4315000
)
[1] => Array
(
[area] => Medan
[jml_trx] => 9
[total_trx] => 3245000
)
[2] => Array
(
[area] => Jakarta
[jml_trx] => 13
[total_trx] => 3225000
)
)

Sedangkan untuk query yang kedua (query per nama) bentuknya


adalah:
Array
(
[0] => Array
(
[nama] => Alfa
[area] => Jakarta
[jml_trx] => 7
[total_trx] => 1565000
)
[1] => Array
(
[nama] => Bravo
[area] => Jakarta

Killer Trik Query MySQL 293


[jml_trx] => 6
[total_trx] => 1660000
)
[2] => Array
(
[nama] => Charlie
[area] => Medan
[jml_trx] => 6
[total_trx] => 1875000
)
)

Dengan demikian, pada query kedua kita perlu ubah indexnya


menjadi per nama area, sehingga hasil query pertama dapat dengan
mudah dihubungkan dengan query kedua. Perhatikan ilustrasi
berikut:

Gambar 15.6 Bentuk Array Hasil Query

294 BAB 15 Table Reporting Dengan PHP dan MySQL


Script yang kita gunakan untuk mengubah index menjadi area
adalah sebagai berikut:

1. while($row = mysqli_fetch_array($query_nama)) {
2. $result_nama[$row['area']][] = $row;
3. }

15.3. Table Reporting II


Pada contoh kedua ini, kita akan menampilkan data rekap absensi
pegawai per hari selama satu bulan.

Pada sisi aplikasi, terdapat opsi untuk memilih bulan dan tahun. Opsi
ini nantinya digunakan untuk mengambil data pada bulan dan tahun
tertentu.

Data dan query yang kita gunakan sama dengan yang digunakan
pada BAB 13 Menguasai DATE TIME sub-bab 13.3 Studi Kasus #3.

Tampilan form yang akan kita buat adalah sebagai berikut:

Gambar 15.7 Tampilan Form Submit

Killer Trik Query MySQL 295


Selanjutnya ketika form disubmit, maka akan muncul tampilan data
tanggal dari 1 s.d tanggal akhir pada bulan yang dipilih beserta
keterangan hadir pegawai seperti tampak pada gambar berikut:

Gambar 15.8. Tampilan Rekap Absensi

Pada tabel hasil, terdapat beberapa keterangan yang kita gunakan,


yaitu:

 TL1, Jika pegawai hadir pukul 07:31 s.d 08:00:00

 TL2, jika pegawai hadir pukul 08:01:00 s.d 12:00:00

 PSW, jika pegawai absen pulang pukul 17:00:00

 TAM, jika pegawai tidak absen masuk

 TAP, jika pegawai tidak absen pulang

 TM, jika pegawai tidak masuk (tidak ada absen masuk maupun
pulang

Tabel Database…
Tabel yang kita gunakan adalah tabel absensi_pegawai dan
absensi_data dengan data sebagai berikut:
absensi_data
+----+------------+---------------------+
| id | id_pegawai | waktu_absen |

296 BAB 15 Table Reporting Dengan PHP dan MySQL


+----+------------+---------------------+
| 1 | 1 | 2017-01-01 07:01:39 |
| 2 | 1 | 2017-01-01 17:23:55 |
| 3 | 2 | 2017-01-01 07:18:56 |
| 4 | 2 | 2017-01-01 17:13:33 |
| 5 | 3 | 2017-01-01 07:08:14 |
| ...| ... | ... |
+----+------------+---------------------+

absensi_pegawai
+------------+---------+-------------+
| id_pegawai | nama | alamat |
+------------+---------+-------------+
| 1 | Alfa | Jakarta |
| 2 | Beta | Semarang |
| 3 | Charlie | Surabaya |
| 4 | Delta | Yogayakarta |
| 5 | Erdhi | Surakarta |
| ... | ... | ... |
+------------+---------+-------------+

Kode FROM HTML


Script PHP yang akan kita gunakan untuk membuat form HTML
adalah sebagai berikut:

1. $year_options = array(2017 => 2017, 2016 => 2016);


2. $month_options = array(1=>'Januari'
3. ,'Februari'
4. ,'Maret'
5. ,'April'
6. ,'Mei'
7. ,'Juni'
8. ,'Juli'
9. ,'Agustus'
10. ,'Septembe'
11. ,'Oktober'
12. ,'November'
13. ,'Desember'
14. );
15. <form action="" method="post">
16. <div class="row">
17. <label>Periode</label>
18. <div class="col-wrap">

Killer Trik Query MySQL 297


19. <select name="bulan">
20. <?php
21. foreach ($month_options as $key => $val) {
22. $selected = @$_POST['bulan'] == $key
23. ? ' selected="selected"'
24. : '';
25. echo '<option value="' . $key . '"' . $selected
26. . '>' . $val . '</option>';
27. }
28. ?>
29. </select>
30. <select name="tahun">
31. <?php
32. foreach ($year_options as $key => $val) {
33. $selected = @$_POST['tahun'] == $key
34. ? ' selected="selected"'
35. : '';
36. echo '<option value="' . $key . '"' . $selected
37. . '>' . $val . '</option>';
38. }?>
39. </select>
40. </div>
41. </div>
42. <div class="row">
43. <input class="offset button" type="submit"
44. name="submit" value="Submit"/>
45. </div>
46. </form>

Pada kode diatas, sama seperti kasus sebelumnya, kita membuat


opsi bulan dan tahun dari array. Hal ini akan memudahkan kita untuk
membuat dan memperbaiki kode, salah satunya memberi atribut
selected="selected" pada opsi yang dipilih ketika form disumbit

Kode HTML dan PHP diatas jika dijalankan akan menghasilkan kode
HTML sebagai berikut:

1. <form action="" method="get">


2. <div class="row">
3. <label>Periode</label>
4. <div class="col-wrap">
5. <select name="bulan">
6. <option value="1">Januari</option>

298 BAB 15 Table Reporting Dengan PHP dan MySQL


7. <option value="2">Februari</option>
8. <option value="3">Maret</option>
9. <option value="4">April</option>
10. <option value="5">MeiMei</option>
11. <option value="6">Juni</option>
12. <option value="7">Juli</option>
13. <option value="8">Agustus</option>
14. <option value="9">Septembe</option>
15. <option value="10">Oktober</option>
16. <option value="11">November</option>
17. <option value="12">Desember</option>
18. </select>
19. <select name="tahun">
20. <option value="2017">2017</option>
21. <option value="2016">2016</option>
22. </select>
23. </div>
24. </div>
25. <div class="row">
26. <input class="offset button" type="submit"
27. name="submit" value="Submit" />
28. </div>
29. </form>

SQL Query …
Selanjutnya ketika form disubmit (tombol submit di klik), kita jalankan
query MySQL untuk mengambil data dari database sesuai dengan
opsi yang dipilih oleh user.

SQL query yang kita gunakan mirip dengan yang kita gunakan pada
BAB 13 subbab 13.2

Jika digabung dengan Kode PHP maka bentuknya seperti berikut:

1. if (isset($_GET['submit']))
2. {
3. require_once('config.php');
4. // KONEKSI
5. $conn = @mysqli_connect(DB_HOST, DB_USER, DB_PASS,
6. DB_NAME);
7. if (!$conn) {

Killer Trik Query MySQL 299


8. $error[] = 'MySQL ERROR : ' .
9. mysqli_connect_error($conn);
10. } else {
11. $sql =
12. 'SELECT id_pegawai,
13. nama,
14. tanggal,
15. jam_masuk,
16. CASE WHEN jam_masuk != "-"
17. THEN
18. CASE WHEN jam_masuk > "07:31:00"
19. AND jam_masuk <= "08:00:00"
20. THEN "TL1"
21. WHEN jam_masuk >= "08:01:00"
22. AND jam_masuk <= "12:00:00"
23. THEN "TL2"
24. ELSE "-"
25. END
26. ELSE "TAM"
27. END AS ket_masuk,
28. jam_pulang,
29. CASE WHEN jam_pulang != "-"
30. THEN IF(jam_pulang <"17:00:00", "PSW", "-")
31. ELSE "TAP"
32. END AS ket_pulang
33. FROM (
34. SELECT id_pegawai,
35. DATE(waktu_absen) AS tanggal,
36. IF (TIME( MIN(waktu_absen) ) <= "12:00:00"
37. , TIME( MIN(waktu_absen) )
38. , "-"
39. ) AS jam_masuk,
40. IF (TIME( MAX(waktu_absen) ) > "12:00:00"
41. , TIME( MAX(waktu_absen) )
42. , "-"
43. )AS jam_pulang
44. FROM absensi_data
45. GROUP BY id_pegawai, DATE(waktu_absen)
46. ORDER BY tanggal, id_pegawai
47. ) AS absensi
48. LEFT JOIN absensi_pegawai USING(id_pegawai)
49. WHERE MONTH(tanggal) = '.$_POST['bulan'].'
50. AND YEAR(tanggal) = ' . $_POST['tahun'];
51. $query = mysqli_query ($conn, $sql);
52. // RESULT
53. if (!$query) {
54.

300 BAB 15 Table Reporting Dengan PHP dan MySQL


55. $error[] = mysqli_error($conn) .
56. '<br/><strong>SQL Query</strong>: ' . $sql;
57. } else {
58. $num_rows = mysqli_num_rows($query);
59. if (!$num_rows) {
60. $warning[] = 'Data tidak ditemukan';
61. } else {
62. $hasil_query = true;
63. }
64. }
65. } //
66. }

Pada script diatas, pada klausa WHERE kita tambahkan scrit WHERE
MONTH(tanggal) = '.$_GET['bulan'].' AND YEAR(tanggal) = '
. $_GET['tahun'];, script tersebut akan memfilter data sesuai
dengan bulan dan tahun yang dipilih. Misal jika data yang dipilih
bulan maret 2017, maka script berubah menjadi WHERE
MONTH(tanggal) = 3 AND YEAR(tanggal) = 2017';

Query diatas akan menghasilkan tabel output sebagai berikut:


+------------+---------+------------+-----------+-----------+------------+------------+
| id_pegawai | nama | tanggal | jam_masuk | ket_masuk | jam_pulang | ket_pulang |
+------------+---------+------------+-----------+-----------+------------+------------+
| ... | ... | ... | ... | ... | ... | ... |
| 14 | Nina | 2017-01-02 | - | TAM | 17:23:42 | - |
| 15 | Ode | 2017-01-02 | 07:03:14 | - | 17:28:18 | - |
| 16 | Priska | 2017-01-02 | 07:20:37 | - | 17:19:34 | - |
| 17 | Rizka | 2017-01-02 | 07:25:48 | - | 17:24:08 | - |
| 18 | Sena | 2017-01-02 | 07:09:09 | - | 17:28:05 | - |
| 19 | Teguh | 2017-01-02 | 07:08:28 | - | 17:02:59 | - |
| 20 | Umar | 2017-01-02 | 07:13:08 | - | 17:16:58 | - |
| 2 | Beta | 2017-01-03 | 07:48:55 | TL1 | 17:10:07 | - |
| ... | ... | ... | ... | ... | ... | ... |
+------------+---------+------------+-----------+-----------+------------+------------+

Menampilkan data
Bagian terpenting adalah bagian menampilkan data, karena bagian
ini akan mengkonversi tabel hasil query menjadi tabel output sesuai
dengan yang telah kita tentukan diawal.

Killer Trik Query MySQL 301


Bentuk tampilan data yang akan kita buat berbeda dengan tabel
output hasil query. Kenapa bentuk hasil query tidak disamakan saja
dengan tabel yang ingin ditampilkan?

Jawabnya, karena adanya keterbatasan query seperti yang telah kita


bahas sebelumnya, disamping itu terdapat beberapa bagian yang
tidak bisa di buat querynya seperti jika pegawai tidak absen sama
sekali pada tanggal tertentu, sehingga pada tanggal tersebut tidak
ada data absen pegawai tersebut pada tabel pegawai, padahal
tanggal tersebut tetap harus ditampilkan.

Script untuk menampilkan data adalah sebagai berikut:

1. if (isset($_POST['submit'])) {
2. if ($hasil_query)
3. {
4. while($row = mysqli_fetch_assoc($query)) {
5. $tgl = (int) substr($row['tanggal'],-2);
6. $result[$row['id_pegawai']]['nama'] = $row['nama'];
7. $result[$row['id_pegawai']][$tgl] = $row;
8. }
9.
10. $jml_hari = date('t');
11. $thead = '<th>No</th>
12. <th>Nama</th>';
13.
14. for($i=1; $i<=$jml_hari;$i++) {
15. $thead .= '<th>'. $i .'</th>';
16. }
17.
18. echo '
19. <div class="table-responsive">
20. <table>
21. <thead>' . $thead . '</thead>
22. <tbody>';
23.
24. $no = 1;
25. foreach($result as $id_pegawai => $hari_absen) {
26. echo '<tr>
27. <td>' . $no . '</td>
28. <td>' . $hari_absen['nama'] . '</td>';
29.

302 BAB 15 Table Reporting Dengan PHP dan MySQL


30. for($i=1; $i<=$jml_hari;$i++) {
31. if(key_exists($i, $hari_absen)) {
32.
33. $ket = array();
34. if ($hari_absen[$i]['ket_masuk'] != '-') {
35. $ket[] =$hari_absen[$i]['ket_masuk'];
36. }
37.
38. if ($hari_absen[$i]['ket_pulang'] != '-') {
39. $ket[] =$hari_absen[$i]['ket_masuk'];
40. }
41.
42. if ($ket) {
43. $text = join($ket, ', ');
44. } else {
45. $text = '-';
46. }
47. } else {
48. $text = 'TM';
49. }
50. echo '<td>'. $text .'</td>';
51. }
52. echo '</tr>';
53. $no++;
54. }
55.
56. echo '</tbody><tfoot>' . $thead . '</tfoot>
57. </table>
58. </div>';
59. }
60. }

Pada query diatas, pertama tama hasil query kita simpan dalam
variabel $result. Variabel ini berisi data array dengan bentuk
sedemikian rupa sehingga memudahkan kita untuk menampilkan
data per tanggal.

Script yang kita gunakan untuk membuat variabel $result adalah


sebagai berikut:

1. while($row = mysqli_fetch_assoc($query)) {
2. $tgl = (int) substr($row['tanggal'],-2);
3. $result[$row['id_pegawai']]['nama'] = $row['nama'];

Killer Trik Query MySQL 303


4. $result[$row['id_pegawai']][$tgl] = $row;
5. }

Bentuk array yang ada pada variabel result adalah sebagai berikut:
Array
(
[1] => Array
(
[nama] => Alfa
[1] => Array
(
[id_pegawai] => 1
[nama] => Alfa
[tanggal] => 2017-02-01
[jam_masuk] => 07:16:32
[ket_masuk] => -
[jam_pulang] => 17:14:04
[ket_pulang] => -
)

[2] => Array


(
[id_pegawai] => 1
[nama] => Alfa
[tanggal] => 2017-02-02
[jam_masuk] => 07:22:21
[ket_masuk] => -
[jam_pulang] => 17:11:13
[ket_pulang] => -
)

[4] => Array


(
[id_pegawai] => 1
[nama] => Alfa
[tanggal] => 2017-02-04
[jam_masuk] => 07:18:25
[ket_masuk] => -
[jam_pulang] => 17:08:20
[ket_pulang] => -
)

[5] => Array


(
[id_pegawai] => 1
[nama] => Alfa
[tanggal] => 2017-02-05
[jam_masuk] => 07:17:43
[ket_masuk] => -
[jam_pulang] => 17:08:37
[ket_pulang] => -

304 BAB 15 Table Reporting Dengan PHP dan MySQL


)

Pada bentuk diatas, index nama menunjukan bahwa kelompok array


tersebut terkait dengan nama pegawai tertentu, index setelah nama
menunjukkan tanggal absen dari nama tersebut, jika tanggal tidak
ada pada index (seperti index 3) berarti nama tersebut tidak absen,
pada contoh diatas, index nya 1,2,4,5 sehingga tanggal 3, pegawai
tersebut tidak absen.

Selanjunya kita loop hari mulai dari tanggal 1 dengan tanggal terakhir
dari bulan yang dipilih. Tanggal terakhir pada bulan yang dipilih
diperoleh dari fungsi date('t', strtotime($_GET['tahun'].'-
'.$_GET['bulan'].'-01')), misal: date('t', strtotime('2017-
03-01')), hasilnya kita simpan pada variabel $jml_hari

Selanjutnya didalam loop tersebut kita cek tanggal pada tiap tiap
pegawai, jika tanggalnya ada, berarti pegawai tersebut absen dan
kita cek ket_masuk dan ket_pulangnya, jika tidak ada, kita beri
keterangan TM, bentuk script PHPnya adalah sebagai berikut:

1. for($i=1; $i<=$jml_hari;$i++) {
2. if(key_exists($i, $hari_absen)) {
3.
4. $ket = array();
5. if ($hari_absen[$i]['ket_masuk'] != '-') {
6. $ket[] =$hari_absen[$i]['ket_masuk'];
7. }
8.
9. if ($hari_absen[$i]['ket_pulang'] != '-') {
10. $ket[] =$hari_absen[$i]['ket_masuk'];
11. }
12.
13. if ($ket) {
14. $text = join($ket, ', ');
15. } else {
16. $text = '-';
17. }
18. } else {
19. $text = 'TM';

Killer Trik Query MySQL 305


20. }
21. echo '<td>'. $text .'</td>';
22. }

Pada query diatas, pengecekan tanggal dilakukan dengan fungsi


key_exists if(key_exists($i, $hari_absen)) baris 2

15.3. Table Reporting III


Pada latihan ketiga ini, kita akan menampilkan data penerimaan
pajak per bulan pada tahun tertentu. Model data ini sama seperti
yang kita bahas pada BAB 14 subbab 14.3

Tampilan form yang akan kita gunakan adalah sebagai berikut:

Gambar 15.9. Tampilan Bentuk Form Submit

306 BAB 15 Table Reporting Dengan PHP dan MySQL


Selanjutnya ketika form disubmit maka akan muncul data
penerimaan per rekening mulai dari bulan Januari s.d Desember
untuk tahun yang dipilih dengan tampilan sebagai berikut:

Gambar 15.10. Tampilan Tabel Hasil Ketika Form di Submit

Tabel Database…
Tabel database yang akan kita gunakan adalah tabel pjk_rekening
dan pjk_realisasi dengan contoh data sebagai berikut:
pjk_rekening
+-------------+----------------+
| id_rekening | nama_rekening |
+-------------+----------------+
| 1 | Pajak Hotel |
| 2 | Pajak Restoran |
| 3 | Pajak Hiburan |
| 4 | Pajak Reklame |
| 5 | Pajak Parkir |
+-------------+----------------+
pjk_realisasi
+----+-------------+------------+-----------+
| id | id_rekening | tanggal | pemasukan |
+----+-------------+------------+-----------+
| 1 | 1 | 2016-01-01 | 13982658 |
| 2 | 1 | 2016-01-02 | 14873263 |
| 3 | 1 | 2016-01-03 | 20594174 |
| 4 | 1 | 2016-01-04 | 22998050 |
| 5 | 1 | 2016-01-05 | 14376610 |
|... | ... | ... | ... |
+----+-------------+------------+-----------+

Killer Trik Query MySQL 307


Kode Form HTML
Kode PHP untuk membuat form HTML adalah sebagai berikut:

1. $year_options = array(2017 => 2017, 2016 => 2016);


2. <form action="" method="get">
3. <div class="row">
4. <label>Jenis Pajak</label>
5. <div class="col-wrap">
6. <select name="jns_rekening">
7. <option value="semua">Semua</option>
8. <?php
9. $sql = 'SELECT id_rekening, nama_rekening
10. FROM pjk_rekening';
11. $query_rekening = mysqli_query($conn, $sql);
12. while ($row =
mysqli_fetch_assoc($query_rekening)) {
13. $selected = @$_GET['jns_rekening']
14. == $row['id_rekening']
15. ? ' selected="selected"' : '';
16. echo '<option value="' .
$row['id_rekening'] . '"' . $selected . '>' .
$row['nama_rekening'] . '</option>';
17. }
18. ?>
19. </select>
20. <select name="tahun">
21. <?php
22. foreach ($year_options as $key => $val) {
23. $selected = @$_POST['tahun'] == $key ? '
selected="selected"' : '';
24. echo '<option value="' . $key . '"' .
$selected . '>' . $val . '</option>';
25. }?>
26. </select>
27. </div>
28. </div>
29. <div class="row">
30. <input class="offset button" type="submit"
31. name="submit" value="Submit"/>
32. </div>
33. </form>

Pada kode diatas, kita membuat opsi jenis rekening dari hasil query
database kemudian dengan loop while kita susun HTML option nya,

308 BAB 15 Table Reporting Dengan PHP dan MySQL


sedangkan untuk tahun, kita menggunakan array, seperti yang kita
gunakan pada contoh sebelumnnya.

Script PHP diatas ketika dijalankan akan menghasilkan kode HTML


sebagai berikut:

1. <form action="" method="get">


2. <div class="row">
3. <label>Jenis Pajak</label>
4. <div class="col-wrap">
5. <select name="jns_rekening">
6. <option value="semua">Semua</option>
7. <option value="1">Pajak Hotel</option>
8. <option value="2">Pajak Restoran</option>
9. <option value="3">Pajak Hiburan</option>
10. <option value="4">Pajak Reklame</option>
11. <option value="5">Pajak Parkir</option>
12. </select>
13. <select name="tahun">
14. <option value="2017">2017</option>
15. <option value="2016">2016</option>
16. </select>
17. </div>
18. </div>
19. <div class="row">
20. <input class="offset button" type="submit" name="submit"
value="Submit" />
21. </div>
22. </form>

SQL Query …
Selanjutnya ketika form disubmit (tombol submit di klik), kita ambil
data dari database sesuai dengan parameter yang dipilih.

Format SQL yang akan kita gunakan adalah sebagai berikut:

1. SELECT nama_rekening
2. , SUM(IF(MONTH(tanggal)=1,pemasukan, NULL)) AS bulan_1
3. , SUM(IF(MONTH(tanggal)=2,pemasukan, NULL)) AS bulan_2
4. , SUM(IF(MONTH(tanggal)=3,pemasukan, NULL)) AS bulan_3
5. , SUM(IF(MONTH(tanggal)=4,pemasukan, NULL)) AS bulan_4

Killer Trik Query MySQL 309


6. , SUM(IF(MONTH(tanggal)=5,pemasukan, NULL)) AS bulan_5
7. , SUM(IF(MONTH(tanggal)=6,pemasukan, NULL)) AS bulan_6
8. , SUM(IF(MONTH(tanggal)=7,pemasukan, NULL)) AS bulan_7
9. , SUM(IF(MONTH(tanggal)=8,pemasukan, NULL)) AS bulan_8
10. , SUM(IF(MONTH(tanggal)=9,pemasukan, NULL)) AS bulan_9
11. , SUM(IF(MONTH(tanggal)=10,pemasukan, NULL)) AS bulan_10
12. , SUM(IF(MONTH(tanggal)=11,pemasukan, NULL)) AS bulan_11
13. , SUM(IF(MONTH(tanggal)=12,pemasukan, NULL)) AS bulan_12
14. , SUM(IF(MONTH(tanggal)=1,pemasukan, NULL)) +
15. SUM(IF(MONTH(tanggal)=2,pemasukan, NULL)) +
16. SUM(IF(MONTH(tanggal)=3,pemasukan, NULL)) +
17. SUM(IF(MONTH(tanggal)=4,pemasukan, NULL)) +
18. SUM(IF(MONTH(tanggal)=5,pemasukan, NULL)) +
19. SUM(IF(MONTH(tanggal)=6,pemasukan, NULL)) +
20. SUM(IF(MONTH(tanggal)=7,pemasukan, NULL)) +
21. SUM(IF(MONTH(tanggal)=8,pemasukan, NULL)) +
22. SUM(IF(MONTH(tanggal)=9,pemasukan, NULL)) +
23. SUM(IF(MONTH(tanggal)=10,pemasukan, NULL)) +
24. SUM(IF(MONTH(tanggal)=11,pemasukan, NULL)) +
25. SUM(IF(MONTH(tanggal)=12,pemasukan, NULL)) AS total
26. FROM pjk_rekening
27. LEFT JOIN pjk_realisasi USING(id_rekening)
28. WHERE YEAR(tanggal) = 2017 AND id_rekening = 1
29. GROUP BY id_rekening

Jika disesuaikan dengan Kode PHP, maka bentuk scriptnya adalah


sebagai berikut:

1. if (isset($_GET['submit']))
2. {
3. $sql = 'SELECT nama_rekening';
4.
5. $total = $kolom_total = '';
6. for($i=1; $i<=12; $i++) {
7. $sql .= ', SUM(IF(MONTH(tanggal)='.$i.',pemasukan,
8. NULL)) AS bulan_' . $i;
9. $kolom_total .=
10. 'SUM(IF(MONTH(tanggal)='.$i.',pemasukan, NULL)) + ';
11. }
12. $sql .= ', ' . rtrim($kolom_total, '+ ') . ' AS total';
13.
14. $sql .= ' FROM pjk_rekening
15. LEFT JOIN pjk_realisasi USING(id_rekening)
16. WHERE YEAR(tanggal) = ' . $_GET['tahun'];
17.

310 BAB 15 Table Reporting Dengan PHP dan MySQL


18. if ($_GET['jns_rekening'] != 'semua') {
19. $sql .= ' AND id_rekening = ' .
20. $_GET['jns_rekening'];
21. }
22.
23. $sql .= ' GROUP BY id_rekening';
24. $query = mysqli_query ($conn, $sql);
25.
26. // RESULT
27. if (!$query) {
28. $error[] = mysqli_error($conn) . '<br/><strong>SQL
29. Query</strong>: ' . $sql;
30. } else {
31. $num_rows = mysqli_num_rows($query);
32. if (!$num_rows) {
33. $warning[] = 'Data tidak ditemukan';
34. } else {
35. $hasil_query = true;
36. }
37. }
38. }

Pada query diatas kita menggunakan loop for (for($i=1; $i<=12;


$i++) untuk menghasilkan kode SQL SUM(IF(...)) AS bulan_1 s.d
SUM(IF(...)) AS bulan_12 dan kode SQL untuk membuat kolom
total. Hal ini akan memudahkan kita dibanding menuliskannya
secara manual.

Pada form terdapat opsi pilihan kode rekening dan tahun, pada
script diatas, kita akomodir opsi tersebut dengan menambahkan
WHERE YEAR(tanggal) = ' . $_GET['tahun'] baris 16 untuk
menangkap parameter tahun dan if ($_GET['jns_rekening'] !=
'semua') baris 18 untuk menangkap opsi jenis rekening

Query database diatas jika dijalankan akan menghasilkan tabel


output sebagai berikut:
+----------------+------------+------------+------------+------------+------------+------------+
| nama_rekening | bulan_1 | bulan_2 | bulan_3 | bulan_4 | bulan_5 | bulan_6 |
+----------------+------------+------------+------------+------------+------------+------------+
| Pajak Hotel | 818445540 | 842745305 | 825186671 | 847634251 | 846523248 | 836003044 |
| Pajak Restoran | 1096179761 | 1134977206 | 1140640523 | 1403283988 | 1367580734 | 1368132153 |

Killer Trik Query MySQL 311


| Pajak Hiburan | 136841318 | 153436852 | 138721452 | 119088595 | 285757615 | 128331427 |
| Pajak Reklame | 567652603 | 608332766 | 559423784 | 553705588 | 530570769 | 585134314 |
| Pajak Parkir | 208565557 | 215429220 | 214271148 | 211938557 | 210450700 | 212189466 |
+----------------+------------+------------+------------+------------+------------+------------+
Lanjutan...
+------------+------------+------------+------------+------------+------------+-------------+
| bulan_7 | bulan_8 | bulan_9 | bulan_10 | bulan_11 | bulan_12 | total |
+------------+------------+------------+------------+------------+------------+-------------+
| 1095435099 | 1077558281 | 1083611992 | 1690403939 | 1746572431 | 1704636454 | 13414756255 |
| 1923988287 | 1951076205 | 1934394620 | 2461604649 | 2509092912 | 2513044613 | 20803995651 |
| 611981385 | 596724627 | 581978568 | 877521775 | 787443493 | 800374063 | 5218201170 |
| 1404470439 | 1402690668 | 1405270154 | 1660125222 | 1616824297 | 1618349940 | 12512550544 |
| 271891506 | 268814575 | 270452915 | 328052173 | 326471993 | 329518045 | 3068045855 |
+------------+------------+------------+------------+------------+------------+-------------+

Menampilkan data
Bentuk tampilan data yang akan kita buat sudah sama dengan tabel
output hasil query, sehingga untuk menampilkannya ke dalam
bentuk tabel html, tidak memerlukan pengolahan lebih lanjut.

Script untuk menampilkan data adalah sebagai berikut:

1. if ($hasil_query)
2. {
3. $thead = '<th>No</th>
4. <th>Rekening</th>';
5.
6. for($i=1; $i<=12;$i++) {
7. $thead .= '<th>'. $bulan[$i] .'</th>';
8. $total_bulan[$i] = 0; // identifikasi awal agar tidak
muncul pesan warning
9. }
10. $thead .= '<th>TOTAL</th>';
11.
12. echo '
13. <div class="table-responsive">
14. <table>
15. <thead>' . $thead . '</thead>
16. <tbody>';
17.
18. $no = 1;
19. $total_all = 0;
20. while($row = mysqli_fetch_assoc($query)) {
21. echo '<tr>
22. <td>' . $no . '</td>
23. <td>' . $row['nama_rekening'] . '</td>';
24. for($i=1; $i<=12;$i++) {
25.

312 BAB 15 Table Reporting Dengan PHP dan MySQL


echo '<td class="right">' .
26. number_format($row['bulan_'.$i], 0, ',', '.') . '</td>';
27. $total_bulan[$i] += $row['bulan_'.$i];
28. }
29. $total_all += $row['total'];
echo '<td class="right">' .
30. number_format($row['total'], 0 ,',', '.') . '</td></tr>';
31.
32. $no++;
33. }
34. echo '<td></td>
35. <td>TOTAL</td>';
36. for($i=1; $i<=12;$i++) {
echo '<td
37. class="right">'.number_format($total_bulan[$i],0,',','.').'</td>';
38. }
echo '<td class="right">' . number_format($total_all, 0,
39. ',', '.') . '</td>';
40.
41. echo '</tbody><tfoot>' . $thead . '</tfoot>
42. </table>
43. </div>';
44. }

Pada script diatas, terdapat variabel $total_bulan, variabel ini


digunaan untuk menambahkan baris total di baris paling bawah, dan
variabel $total_all untuk menambahkan baris total di pojok kanan
bawah.

Pada pembahasan bab bab terdahulu, telah kita bahas


bagaimana membuat baris total dengan union dan with
rollup. Sebagai alternatif, kita dapat membuatnya pada sisi
aplikasi (untuk mengurangi kompleksitas query). Kelemahan
dari metode ini, jika kita ganti bahasa pemrogramannya,
maka kita perlu menyesuaikan scriptnya, namun jika kita
tidak berencana mengganti bahasa program, metode diatas
aman untuk digunakan.

Killer Trik Query MySQL 313


Lebih Lanjut…
Lebih lanjut, sering kita jumpai table report dengan parameter
berbeda meskipun bentuknya hampir sama.

Misal pada contoh diatas, terdapat opsi untuk memilih beberapa


tahun sekaligus. Jika tahun yang dipilih lebih dari satu, maka akan
ditampilkan data penerimaan per bulan disandingkan antar tahun
dan di tampilkan persentase pertumbuhannya.

Namun jika tahun yang dipilih hanya satu, maka tampilannya sama
seperti pada contoh sebelumnya

Berikut ini bentuk tampilan FORM nya:

Gambar 15.11. Bentuk Form Dengan Opsi Pilihan Beberapa Tahun

Ketika disubmit dan yang dientang tahun 2017 dan 2016 maka akan
terbentuk query seperti berikut ini:

1. SELECT nama_rekening
2. , bulan_1_2017
3. , ROUND( (bulan_1_2017 - bulan_1_2016)
4. / bulan_1_2016 * 100, 2) AS persen_1_2017
5. , bulan_2_2017
6. , ROUND( (bulan_2_2017 - bulan_2_2016)

314 BAB 15 Table Reporting Dengan PHP dan MySQL


7. / bulan_2_2016 * 100, 2) AS persen_2_2017
8. , bulan_3_2017
9. , ROUND( (bulan_3_2017 - bulan_3_2016)
10. / bulan_3_2016 * 100, 2) AS persen_3_2017
11. , bulan_4_2017
12. , ROUND( (bulan_4_2017 - bulan_4_2016)
13. / bulan_4_2016 * 100, 2) AS persen_4_2017
14. , bulan_5_2017
15. , ROUND( (bulan_5_2017 - bulan_5_2016)
16. / bulan_5_2016 * 100, 2) AS persen_5_2017
17. , bulan_6_2017
18. , ROUND( (bulan_6_2017 - bulan_6_2016)
19. / bulan_6_2016 * 100, 2) AS persen_6_2017
20. , bulan_7_2017
21. , ROUND( (bulan_7_2017 - bulan_7_2016)
22. / bulan_7_2016 * 100, 2) AS persen_7_2017
23. , bulan_8_2017
24. , ROUND( (bulan_8_2017 - bulan_8_2016)
25. / bulan_8_2016 * 100, 2) AS persen_8_2017
26. , bulan_9_2017
27. , ROUND( (bulan_9_2017 - bulan_9_2016)
28. / bulan_9_2016 * 100, 2) AS persen_9_2017
29. , bulan_10_2017
30. , ROUND( (bulan_10_2017 - bulan_10_2016)
31. / bulan_10_2016 * 100, 2) AS persen_10_2017
32. , bulan_11_2017
33. , ROUND( (bulan_11_2017 - bulan_11_2016)
34. / bulan_11_2016 * 100, 2) AS persen_11_2017
35. , bulan_12_2017
36. , ROUND( (bulan_12_2017 - bulan_12_2016)
37. / bulan_12_2016 * 100, 2) AS persen_12_2017
38. , total_2017
39. , ROUND( (total_2017 - total_2016)
40. / total_2016 * 100, 2) AS persen_2017,
41. bulan_1_2016
42. , bulan_2_2016
43. , bulan_3_2016
44. , bulan_4_2016
45. , bulan_5_2016
46. , bulan_6_2016
47. , bulan_7_2016
48. , bulan_8_2016
49. , bulan_9_2016
50. , bulan_10_2016
51. , bulan_11_2016
52. , bulan_12_2016
53. , total_2016

Killer Trik Query MySQL 315


54. FROM pjk_rekening
55. LEFT JOIN (
56. SELECT id_rekening
57. , SUM(IF(MONTH(tanggal)=1 AND YEAR(tanggal) = 2017
58. , pemasukan, NULL)) AS bulan_1_2017
59. , SUM(IF(MONTH(tanggal)=2 AND YEAR(tanggal) = 2017
60. , pemasukan, NULL)) AS bulan_2_2017
61. , SUM(IF(MONTH(tanggal)=3 AND YEAR(tanggal) = 2017
62. , pemasukan, NULL)) AS bulan_3_2017
63. , SUM(IF(MONTH(tanggal)=4 AND YEAR(tanggal) = 2017
64. , pemasukan, NULL)) AS bulan_4_2017
64. , SUM(IF(MONTH(tanggal)=5 AND YEAR(tanggal) = 2017
66. , pemasukan, NULL)) AS bulan_5_2017
67. , SUM(IF(MONTH(tanggal)=6 AND YEAR(tanggal) = 2017
68. , pemasukan, NULL)) AS bulan_6_2017
70. , SUM(IF(MONTH(tanggal)=7 AND YEAR(tanggal) = 2017
71. , pemasukan, NULL)) AS bulan_7_2017
72. , SUM(IF(MONTH(tanggal)=8 AND YEAR(tanggal) = 2017
73. , pemasukan, NULL)) AS bulan_8_2017
74. , SUM(IF(MONTH(tanggal)=9 AND YEAR(tanggal) = 2017
75. , pemasukan, NULL)) AS bulan_9_2017
76. , SUM(IF(MONTH(tanggal)=10 AND YEAR(tanggal) =
77. 2017
78. , pemasukan, NULL)) AS bulan_10_2017
79. , SUM(IF(MONTH(tanggal)=11 AND YEAR(tanggal) =
80. 2017
81. , pemasukan, NULL)) AS bulan_11_2017
82. , SUM(IF(MONTH(tanggal)=12 AND YEAR(tanggal) =
83. 2017
84. , pemasukan, NULL)) AS bulan_12_2017
85. , SUM(IF(MONTH(tanggal)=1 AND YEAR(tanggal) = 2017
86. ,pemasukan, NULL)) +
87. SUM(IF(MONTH(tanggal)=2 AND YEAR(tanggal) = 2017
88. ,pemasukan, NULL)) +
89. SUM(IF(MONTH(tanggal)=3 AND YEAR(tanggal) = 2017
90. ,pemasukan, NULL)) +
91. SUM(IF(MONTH(tanggal)=4 AND YEAR(tanggal) = 2017
92. ,pemasukan, NULL)) +
93. SUM(IF(MONTH(tanggal)=5 AND YEAR(tanggal) = 2017
94. ,pemasukan, NULL)) +
95. SUM(IF(MONTH(tanggal)=6 AND YEAR(tanggal) = 2017
96. ,pemasukan, NULL)) +
97. SUM(IF(MONTH(tanggal)=7 AND YEAR(tanggal) = 2017
98. ,pemasukan, NULL)) +
99. SUM(IF(MONTH(tanggal)=8 AND YEAR(tanggal) = 2017
100. ,pemasukan, NULL)) +
101. SUM(IF(MONTH(tanggal)=9 AND YEAR(tanggal) = 2017

316 BAB 15 Table Reporting Dengan PHP dan MySQL


102. ,pemasukan, NULL)) +
103. SUM(IF(MONTH(tanggal)=10 AND YEAR(tanggal) =
104. 2017
105. ,pemasukan, NULL)) +
106. SUM(IF(MONTH(tanggal)=11 AND YEAR(tanggal) =
107. 2017
108. ,pemasukan, NULL)) +
109. SUM(IF(MONTH(tanggal)=12 AND YEAR(tanggal) =
110. 2017
111. ,pemasukan, NULL)) AS total_2017
112. , SUM(IF(MONTH(tanggal)=1 AND YEAR(tanggal) = 2016
113. , pemasukan, NULL)) AS bulan_1_2016
114. , SUM(IF(MONTH(tanggal)=2 AND YEAR(tanggal) = 2016
115. , pemasukan, NULL)) AS bulan_2_2016
116. , SUM(IF(MONTH(tanggal)=3 AND YEAR(tanggal) = 2016
117. , pemasukan, NULL)) AS bulan_3_2016
118. , SUM(IF(MONTH(tanggal)=4 AND YEAR(tanggal) = 2016
119. , pemasukan, NULL)) AS bulan_4_2016
120. , SUM(IF(MONTH(tanggal)=5 AND YEAR(tanggal) = 2016
121. , pemasukan, NULL)) AS bulan_5_2016
122. , SUM(IF(MONTH(tanggal)=6 AND YEAR(tanggal) = 2016
123. , pemasukan, NULL)) AS bulan_6_2016
124. , SUM(IF(MONTH(tanggal)=7 AND YEAR(tanggal) = 2016
125. , pemasukan, NULL)) AS bulan_7_2016
126. , SUM(IF(MONTH(tanggal)=8 AND YEAR(tanggal) = 2016
127. , pemasukan, NULL)) AS bulan_8_2016
128. , SUM(IF(MONTH(tanggal)=9 AND YEAR(tanggal) = 2016
129. , pemasukan, NULL)) AS bulan_9_2016
130. , SUM(IF(MONTH(tanggal)=10 AND YEAR(tanggal) =
131. 2016
132. , pemasukan, NULL)) AS bulan_10_2016
133. , SUM(IF(MONTH(tanggal)=11 AND YEAR(tanggal) =
134. 2016
135. , pemasukan, NULL)) AS bulan_11_2016
136. , SUM(IF(MONTH(tanggal)=12 AND YEAR(tanggal) =
137. 2016
138. , pemasukan, NULL)) AS bulan_12_2016
139. , SUM(IF(MONTH(tanggal)=1 AND YEAR(tanggal) = 2016
140. ,pemasukan, NULL)) +
141. SUM(IF(MONTH(tanggal)=2 AND YEAR(tanggal) = 2016
142. ,pemasukan, NULL)) +
143. SUM(IF(MONTH(tanggal)=3 AND YEAR(tanggal) = 2016
144. ,pemasukan, NULL)) +
145. SUM(IF(MONTH(tanggal)=4 AND YEAR(tanggal) = 2016
146. ,pemasukan, NULL)) +
147. SUM(IF(MONTH(tanggal)=5 AND YEAR(tanggal) = 2016
148. ,pemasukan, NULL)) +

Killer Trik Query MySQL 317


149. SUM(IF(MONTH(tanggal)=6 AND YEAR(tanggal) = 2016
150. ,pemasukan, NULL)) +
151. SUM(IF(MONTH(tanggal)=7 AND YEAR(tanggal) = 2016
152. ,pemasukan, NULL)) +
153. SUM(IF(MONTH(tanggal)=8 AND YEAR(tanggal) = 2016
154. ,pemasukan, NULL)) +
155. SUM(IF(MONTH(tanggal)=9 AND YEAR(tanggal) = 2016
156. ,pemasukan, NULL)) +
157. SUM(IF(MONTH(tanggal)=10 AND YEAR(tanggal) =
158. 2016
159. ,pemasukan, NULL)) +
160. SUM(IF(MONTH(tanggal)=11 AND YEAR(tanggal) =
161. 2016
162. ,pemasukan, NULL)) +
163. SUM(IF(MONTH(tanggal)=12 AND YEAR(tanggal) =
164. 2016
165. ,pemasukan, NULL)) AS total_2016
166. FROM pjk_realisasi
167. WHERE YEAR(tanggal) = 2017 OR YEAR(tanggal) = 2016
168. GROUP BY id_rekening
169. ) AS realisasi USING(id_rekening)

Jika dikonversi ke script php, maka diperoleh bentuk seperti berikut


ini:

1. if (isset($_GET['submit']))
2. {
3. // CEK APAKAH ADA TAHUN YANG DICETANG
4. $form_error = '';
5. $year_checked = false;
6. foreach ($year_options as $year) {
7. if (key_exists($year, $_GET)) {
8. $year_checked = true;
9. $used_year[] = $year;
10. }
11. }
12. if (!$year_checked) {
13. $error[] = 'Tahun harus dipilih';
14. }
15.
16. // Query
17. $select = '';
18. $subquery = 'SELECT id_rekening';
19.
20. $total = '';

318 BAB 15 Table Reporting Dengan PHP dan MySQL


21. arsort($used_year);
22. $keys = array_keys($used_year);
23. foreach($used_year as $key => $year)
24. {
25. $kolom_total = '';
26. for($i=1; $i<=12; $i++) {
27. // Subquery
28. $subquery .= ', SUM(IF(MONTH(tanggal)='.$i.'
29. AND YEAR(tanggal) = ' . $year . "\r\n"
30. . ', pemasukan, NULL))
31. AS bulan_' . $i . '_'.$year. "\r\n";
32.
33. // Total
34. $kolom_total .= 'SUM(IF(MONTH(tanggal)='.$i.'
35. AND YEAR(tanggal) = ' . $year . "\r\n"
36. . ',pemasukan, NULL)) + '.
37. "\r\n";
38.
39. // Main Select
40. $select .= ', bulan_' . $i . '_'.$year.
41. "\r\n";
42.
43. // Persentase
44. if ($key < max($keys)) {
45. $select .= ', ROUND( (bulan_' . $i .
46. '_'.$year.' - bulan_' . $i . '_'.$used_year[$key+1] . ')
47. / bulan_' . $i . '_'.$used_year[$key+1] . ' * 100, 2) AS
48. persen_'.$i.'_'.$year. "\r\n";
49. }
50. }
51. $subquery .= ', ' . rtrim($kolom_total, '+ '.
52. "\r\n") . ' AS total_' . $year . "\r\n";
53. $select .= ', total_' .$year;
54. if ($key < max($keys)) {
55. $select .= ', ROUND( (total_'.$year.' -
56. total_' .$used_year[$key+1] . ')
57. / total_'.$used_year[$key+1] . ' * 100, 2) AS
58. persen_'.$year;
59. }
60.
61. }
62. $subquery .= 'FROM pjk_realisasi';
63. $subquery .= ' WHERE ';
64.
64. $where_tahun = '';
66. foreach($used_year as $year) {
67.

Killer Trik Query MySQL 319


68. $where_tahun .= 'YEAR(tanggal) = ' . $year . ' OR
70. ';
71. }
72. $subquery .= rtrim($where_tahun, ' OR ');
73.
74. if ($_GET['jns_rekening'] != 'semua') {
75. $subquery .= ' AND id_rekening = ' .
76. $_GET['jns_rekening'];
77. }
78.
79. $subquery .= ' GROUP BY id_rekening';
80.
81. $where = @$_GET['jns_rekening'] != 'semua' ? ' WHERE
82. id_rekening = ' . $_GET['jns_rekening'] : '';
83. $sql = 'SELECT nama_rekening '.$select.'
84. FROM pjk_rekening
85. LEFT JOIN (' . $subquery . ') AS realisasi
86. USING(id_rekening)' . $where;
87.
88.
89. $query = mysqli_query ($conn, $sql);
90.
91. // RESULT
92. if (!$query) {
93. $error[] = mysqli_error($conn) . '<br/><strong>SQL
94. Query</strong>: ' . $sql;
95. } else {
96. $num_rows = mysqli_num_rows($query);
97. if (!$num_rows) {
98. $warning[] = 'Data tidak ditemukan';
99. } else {
100. $hasil_query = true;
101. }
102. }
103. }

Pada script diatas, tahun yang dicentang kita simpan pada variabel
$used_year dengan bentuk array.

320 BAB 15 Table Reporting Dengan PHP dan MySQL


15.4. Table Reporting IV
Pada bentuk table reporting kali ini, kita akan menyandingkan data
target pajak dan realisasi pajak untuk tahun tertentu, dengan
menampilkan data target, realisasi, dan pencapaian dalam persen.

Selain itu, kita tambahkan fitur pada form yang memungkin user
untuk memilih model pengurutan data, apakah data diurutkan
berdasarkan realisasi dalam rupiah, persentase pencapaian, atau
nama rekening (jenis pajak) baik secara ascending maupun
descending.

Tampilan form HTML nya adalah sebagai berikut:

Gambar 15.12. Tampilan Form Submit

Selanjutnya, ketika form disubmit, maka akan muncul tabel seperti


gambar berikut:

Killer Trik Query MySQL 321


Gambar 15.13. Tampilan Tabel Ketika Form di Submit

Tabel Database
Tabel database yang digunakan adalah tabel pjk_rekening, pjk_target
dan pjk_realisasi dengan data sebagai berikut:
pjk_rekening
+-------------+----------------+
| id_rekening | nama_rekening |
+-------------+----------------+
| 1 | Pajak Hotel |
| 2 | Pajak Restoran |
| 3 | Pajak Hiburan |
| 4 | Pajak Reklame |
| 5 | Pajak Parkir |
+-------------+----------------+
Pjk_target
+----+-------------+-------+----------+------------+
| id | id_rekening | tahun | triwulan | target |
+----+-------------+-------+----------+------------+
| 1 | 1 | 2017 | 1 | 3345000000 |
| 2 | 1 | 2017 | 2 | 3150000000 |
| 3 | 1 | 2017 | 3 | 2725000000 |
| 4 | 1 | 2017 | 4 | 4074000000 |
|... | ... | ... | ... | ... |

322 BAB 15 Table Reporting Dengan PHP dan MySQL


+----+-------------+-------+----------+------------+
pjk_realisasi
+----+-------------+------------+-----------+
| id | id_rekening | tanggal | pemasukan |
+----+-------------+------------+-----------+
| 1 | 1 | 2016-01-01 | 13982658 |
| 2 | 1 | 2016-01-02 | 14873263 |
| 3 | 1 | 2016-01-03 | 20594174 |
| 4 | 1 | 2016-01-04 | 22998050 |
| 5 | 1 | 2016-01-05 | 14376610 |
|... | ... | ... | ... |
+----+-------------+------------+-----------+

Kode Form HTML


Script PHP yang kita gunakan untuk membuat form HTML adalah
sebagai berikut:

1. <form action="" method="get">


2. <div class="row">
3. <label>Jenis Pajak</label>
4. <div class="col-wrap">
5. <select name="jns_rekening">
6. <option value="semua">Semua</option>
7. <?php
8. $sql = 'SELECT id_rekening, nama_rekening
9. FROM pjk_rekening';
10. $query_rekening = mysqli_query($conn, $sql);
11. while ($row = mysqli_fetch_assoc($query_rekening))
12. {
13. $selected = @$_GET['jns_rekening'] ==
$row['id_rekening'] ? ' selected="selected"' : '';
14. echo '<option value="' . $row['id_rekening']
. '"' . $selected . '>' . $row['nama_rekening'] . '</option>';
15. }
16. ?>
17. </select>
18. </div>
19. </div>
20. <div class="row">
21. <label>Tahun</label>
22. <div class="col-wrap">
23. <select name="tahun">
24. <?php
25. foreach ($year_options as $key => $val) {
26.

Killer Trik Query MySQL 323


$selected = @$_GET['tahun'] == $key ? '
27. selected="selected"' : '';
echo '<option value="' . $key . '"' .
28. $selected . '>' . $val . '</option>';
29. }?>
30. </select>
31. </div>
32. </div>
33. <div class="row">
34. <label>Urutkan</label>
35. <div class="col-wrap">
36. <select name="order_by">
37. <?php $order_by = array('realisasi' => 'Realisasi'
38. , 'persen' => 'Persen Realisasi'
39. , 'nama_rekening' => 'Nama Rekening'
40. );
41. foreach ($order_by as $key => $val) {
$selected = @$_GET['order_by'] == $key ? '
42. selected="selected"' : '';
echo '<option value="' . $key . '"' .
43. $selected . '>' . $val . '</option>';
44. }?>
45. </select>
46. <select name="sort">
47. <?php $sort = array('ASC', 'DESC');
48. foreach ($sort as $val) {
$selected = @$_GET['sort'] == $val ? '
49. selected="selected"' : '';
echo '<option value="' . $val . '"' .
50. $selected . '>' . $val . '</option>';
51. }?>
52. </select>
53. </div>
54. </div>
55. <div class="row">
<input class="offset button" type="submit"
56. name="submit" value="Submit"/>
57. </div>
58. </form>

Script diatas, untuk membuat opsi jenis rekening, kita ambil data
tabel pjk_rekening kemudian dengan loop while kita susun HTML
option nya, sedangkan opsi yang lain, seperti tahun dan urutkan, kita
gunakan array.

324 BAB 15 Table Reporting Dengan PHP dan MySQL


Script PHP diatas ketika dijalankan akan menghasilkan kode HTML
sebagai berikut:

1. <form action="" method="get">


2. <div class="row">
3. <label>Jenis Pajak</label>
4. <div class="col-wrap">
5. <select name="jns_rekening">
6. <option value="semua">Semua</option>
7. <option value="1">Pajak Hotel</option>
8. <option value="2">Pajak Restoran</option>
9. <option value="3">Pajak Hiburan</option>
10. <option value="4">Pajak Reklame</option>
11. <option value="5">Pajak Parkir</option>
12. </select>
13. </div>
14. </div>
15. <div class="row">
16. <label>Tahun</label>
17. <div class="col-wrap">
18. <select name="tahun">
19. <option value="2017">2017</option>
20. <option value="2016">2016</option>
21. </select>
22. </div>
23. </div>
24. <div class="row">
25. <label>Urutkan</label>
26. <div class="col-wrap">
27. <select name="order_by">
28. <option value="realisasi">Realisasi</option>
29. <option value="persen">Persen
30. Realisasi</option>
<option value="nama_rekening">Nama
31. Rekening</option>
32. </select>
33. <select name="sort">
34. <option value="ASC">ASC</option>
35. <option value="DESC">DESC</option>
36. </select>
37. </div>
38. </div>
39. <div class="row">
<input class="offset button" type="submit" name="submit"
40. value="Submit" />
41. </div>

Killer Trik Query MySQL 325


</form>

SQL Query
Selanjutnya ketika form disubmit (tombol submit di klik), aplikasi
akan mengambil data dari database sesuai dengan parameter yang
dipilih oleh user

Pada table reporting ini, kita menggunakan dua query, yaitu query
untuk total per rekening dan query untuk rekap per triwulan, kenapa
perlu dua query?

Seperti yang telah kita bahas pada bab 14.4 bahwa query memiliki
keterbatasan, tidak semua permasalahan dapat diselesaikan hanya
menggunakan query.

Pada kasus ini, kita akan mengurutkan data hanya pada total masing
masing jenis pajak berdasarkan jumlah penerimaan, persen
pencapaian, atau nama jenis pajak, sedangkan untuk triwulannya
kita urutkan tersendiri.

Perhatikan ilustrasi berikut:

326 BAB 15 Table Reporting Dengan PHP dan MySQL


Gambar 15.14. Ilustrasi Urutan Data Tabel Hasil Query

Model pengurutan seperti ini tidak dapat kita lakukan menggunakan


SQL, karena pada SQL, jika kita ingin mengurutkan data suatu kolom,
maka kita harus mengurutkan semuanya, tidak bisa secara parsial,
untuk itu kita perlu menggunakan dua query, pertama query untuk
total masing masing jenis pajak dan yang kedua query untuk
triwulan.

Kasus diatas sama seperti kasus pada BAB 15.2 Table Reporting I
dimana kita mengurutkan data sekaligus berdasarkan sub total dan
nama sales

Pada SQL, dalam satu kolom, kita harus mengurutkan data


secara keseluruhan, ascending semua atau descending

Killer Trik Query MySQL 327


semua, tidak bisa secara parsial seperti kasus diatas. Dalam
praktik Anda akan banyak menemui kasus seperti ini.

Query pertama
Query pertama adalah query untuk menghitung total masing masing
jenis pajak. Querynya adalah sebagai berikut:

1. SELECT id_rekening, nama_rekening, target, realisasi


2. , ROUND (realisasi / target * 100, 2) AS persen
3. FROM pjk_rekening
4. LEFT JOIN
5. ( SELECT id_rekening, SUM(target) AS target
6. FROM pjk_target
7. WHERE tahun = 2017
8. GROUP BY id_rekening
9. ) AS target USING(id_rekening)
10. LEFT JOIN
11. (
12. SELECT id_rekening, SUM(pemasukan) AS realisasi
13. FROM pjk_realisasi
14. WHERE YEAR(tanggal) = 2017
15. GROUP BY id_rekening
16. ) AS realisasi USING (id_rekening)
17. ORDER BY realisasi DESC

Jika diubah konversi ke script PHP, bentuknya menjadi seperti


berikut:

1. $where_rekening = $_GET['jns_rekening'] == 'semua' ? '' : ' AND


2. id_rekening = "' . $_GET['jns_rekening'] . '" ';
3.
4. $sql_total =
5. 'SELECT id_rekening, nama_rekening, target, realisasi
6. , ROUND (realisasi / target * 100, 2) AS persen
7. FROM pjk_rekening
8. LEFT JOIN
9. ( SELECT id_rekening, SUM(target) AS target
10. FROM pjk_target
11. WHERE tahun = '.$_GET['tahun']. $where_rekening . '
12. GROUP BY id_rekening
13. ) AS target USING(id_rekening)

328 BAB 15 Table Reporting Dengan PHP dan MySQL


14. LEFT JOIN
15. (
16. SELECT id_rekening, SUM(pemasukan) AS realisasi
17. FROM pjk_realisasi
18. WHERE YEAR(tanggal)='.$_GET['tahun'].$where_rekening.'
19. GROUP BY id_rekening
20. ) AS realisasi USING (id_rekening)
21. WHERE 1 = 1 ' . $where_rekening . '
22. ORDER BY ' . $_GET['order_by'] . ' ' . $_GET['sort'];

Pada script diatas, kita simpan opsi pilihan jenis rekening pada
variabel $where_rekening karena opsi tersebut mengandung kondisi
dimana jika jenis rekening tidak bernilai semua, maka kita isi variabel
tersebut dengan kondisi AND id_rekening = "' .
$_GET['jns_rekening'] . '" '

Pada baris 22, kita gunakan WHERE 1=1. Hal ini merupakan trik yang
umum digunakan untuk memudahkan mendefinisikan klausa where
yang berubah ubah. Pada contoh diatas, kita variabel
$where_rekening bernilai kosong, maka klausa WHERE berbentuk
WHERE 1=1, jika tidak maka isinya WHERE 1=1 AND id_rekening = 1.
Jika tanpa 1=1, maka klausa WHERE menjadi WHERE AND
id_rekening = 1, hal ini akan mengakibatkan query error.

SQL pada script diatas kita simpan kedalam variabel $sql_total,


kemudian kita eksekusi SQL tersebut dan hasilnya kita simpan pada
variabel $query_total.

Jika query diatas dijalankan, maka tabel output yang kita peroleh
adalah:
+-------------+----------------+-------------+-------------+--------+
| id_rekening | nama_rekening | target | realisasi | persen |
+-------------+----------------+-------------+-------------+--------+
| 2 | Pajak Restoran | 20711000000 | 20803995651 | 100.45 |
| 1 | Pajak Hotel | 13294000000 | 13414756255 | 100.91 |
| 4 | Pajak Reklame | 12156000000 | 12512550544 | 102.93 |
| 3 | Pajak Hiburan | 4619200000 | 5218201170 | 112.97 |
| 5 | Pajak Parkir | 3040900000 | 3068045855 | 100.89 |
+-------------+----------------+-------------+-------------+--------+

Killer Trik Query MySQL 329


Query kedua
Query kedua kita gunakan untuk mengambil data per triwulan.
Querynya adalah sebagai berikut:

1. SELECT id_rekening
2. , CONCAT("Triwulan ", triwulan) AS triwulan
3. , target
4. , realisasi, ROUND(realisasi/target*100, 2) AS persen
5. FROM pjk_rekening
6. LEFT JOIN (
7. SELECT *
8. FROM pjk_target
9. WHERE tahun = 2017
10. ) AS target USING(id_rekening)
11. LEFT JOIN
12. (
13. SELECT id_rekening,
14. CASE WHEN tanggal >= "2017-01-01"
15. AND tanggal <= "2017-03-31"
16. THEN 1
17. WHEN tanggal >= "2017-04-01"
18. AND tanggal <= "2017-06-31"
19. THEN 2
20. WHEN tanggal >= "2017-07-01"
21. AND tanggal <= "2017-09-31"
22. THEN 3
23. WHEN tanggal >= "2017-10-01"
24. AND tanggal <= "2017-12-31"
25. THEN 4
26. END AS triwulan
27. ,SUM(pemasukan) AS realisasi
28. FROM pjk_realisasi
29. WHERE YEAR(tanggal) = 2017
30. GROUP BY id_rekening,
31. CASE WHEN tanggal >= "2017-01-01"
32. AND tanggal <= "2017-03-31"
33. THEN 1
34. WHEN tanggal >= "2017-04-01"
35. AND tanggal <= "2017-06-31"
36. THEN 2
37. WHEN tanggal >= "2017-07-01"
38. AND tanggal <= "2017-09-31"
39. THEN 3
40. WHEN tanggal >= "2017-10-01"

330 BAB 15 Table Reporting Dengan PHP dan MySQL


41. AND tanggal <= "2017-12-31"
42. THEN 4
43. END
44. ORDER BY id_rekening, triwulan
45. ) AS realisasi USING (id_rekening, triwulan)

Bentuk query diatas sudah kita bahas pada bab 14.3. Jika dikonversi
ke script PHP, bentuknya menjadi:

1. $sql_triwulan =
2. 'SELECT id_rekening
3. , CONCAT("Triwulan ", triwulan) AS triwulan
4. , target
5. , realisasi, ROUND(realisasi/target*100, 2) AS persen
6. FROM pjk_rekening
7. LEFT JOIN (
8. SELECT *
9. FROM pjk_target
10. WHERE tahun = '.$_GET['tahun'] . $where_rekening . '
11. ) AS target USING(id_rekening)
12. LEFT JOIN
13. (
14. SELECT id_rekening,
15. CASE WHEN tanggal >= "'. $_GET['tahun'] .'-01-01"
16. AND tanggal <= "'. $_GET['tahun'] .'-03-31"
17. THEN 1
18. WHEN tanggal >= "'. $_GET['tahun'] .'-04-01"
19. AND tanggal <= "'. $_GET['tahun'] .'-06-31"
20. THEN 2
21. WHEN tanggal >= "'. $_GET['tahun'] .'-07-01"
22. AND tanggal <= "'. $_GET['tahun'] .'-09-31"
23. THEN 3
24. WHEN tanggal >= "'. $_GET['tahun'] .'-10-01"
25. AND tanggal <= "'. $_GET['tahun'] .'-12-31"
26. THEN 4
27. END AS triwulan
28. ,SUM(pemasukan) AS realisasi
29. FROM pjk_realisasi
30. WHERE YEAR(tanggal)='.$_GET['tahun']. $where_rekening . '
31. GROUP BY id_rekening,
32. CASE WHEN tanggal >= "'. $_GET['tahun'] .'-01-01"
33. AND tanggal <= "'. $_GET['tahun'] .'-03-31"
34. THEN 1
35. WHEN tanggal >= "'. $_GET['tahun'] .'-04-01"
36. AND tanggal <= "'. $_GET['tahun'] .'-06-31"

Killer Trik Query MySQL 331


37. THEN 2
38. WHEN tanggal >= "'. $_GET['tahun'] .'-07-01"
39. AND tanggal <= "'. $_GET['tahun'] .'-09-31"
40. THEN 3
41. WHEN tanggal >= "'. $_GET['tahun'] .'-10-01"
42. AND tanggal <= "'. $_GET['tahun'] .'-12-31"
43. THEN 4
44. END
45. ORDER BY id_rekening, triwulan
46. ) AS realisasi USING (id_rekening, triwulan)
47. WHERE 1=1 ' . $where_rekening;
48.
49. $query_triwulan = mysqli_query ($conn, $sql_triwulan);

Pada script diatas, kita simpan SQL kedalam variabel $sql_triwulan,


kemudian kita eksekusi SQL tersebut dan hasilnya kita simpan pada
variabel $query_triwulan.

Nilai varibael $where_rekening pada script diatas mengikuti nilai


pada script sebelumnya.

Jika query diatas dijalankan, maka akah menghasilkan tabel output


sebagai berikut:
+-------------+------------+------------+------------+--------+
| id_rekening | triwulan | target | realisasi | persen |
+-------------+------------+------------+------------+--------+
| 1 | Triwulan 1 | 3345000000 | 2486377516 | 74.33 |
| 1 | Triwulan 2 | 3150000000 | 2530160543 | 80.32 |
| 1 | Triwulan 3 | 2725000000 | 3256605372 | 119.51 |
| 1 | Triwulan 4 | 4074000000 | 5141612824 | 126.21 |
| 2 | Triwulan 1 | 4250000000 | 3371797490 | 79.34 |
| 2 | Triwulan 2 | 4725000000 | 4138996875 | 87.60 |
| 2 | Triwulan 3 | 5304000000 | 5809459112 | 109.53 |
| 2 | Triwulan 4 | 6432000000 | 7483742174 | 116.35 |
| 3 | Triwulan 1 | 1125000000 | 428999622 | 38.13 |
| 3 | Triwulan 2 | 921200000 | 533177637 | 57.88 |
| 3 | Triwulan 3 | 1250000000 | 1790684580 | 143.25 |
| 3 | Triwulan 4 | 1323000000 | 2465339331 | 186.34 |
| 4 | Triwulan 1 | 2535000000 | 1735409153 | 68.46 |
| 4 | Triwulan 2 | 2243000000 | 1669410671 | 74.43 |
| 4 | Triwulan 3 | 3653000000 | 4212431261 | 115.31 |
| 4 | Triwulan 4 | 3725000000 | 4895299459 | 131.42 |

332 BAB 15 Table Reporting Dengan PHP dan MySQL


| 5 | Triwulan 1 | 725900000 | 638265925 | 87.93 |
| 5 | Triwulan 2 | 696400000 | 634578723 | 91.12 |
| 5 | Triwulan 3 | 753200000 | 811158996 | 107.70 |
| 5 | Triwulan 4 | 865400000 | 984042211 | 113.71 |
+-------------+------------+------------+------------+--------+

Menampilkan data
Selanjutnya kita buat script untuk menampilkan data. Untuk
menggabungkan hasil dari kedua query, maka kita perlu
menghubungkan keduanya menggunakan index id_rekening. Pada
query pertama (total per rekening), hasil query dari script PHP
berbentuk sebagai berikut:
Array
(
[0] => Array
(
[id_rekening] => 3
[nama_rekening] => Pajak Hiburan
[target] => 4619200000
[realisasi] => 5218201170
[persen] => 112.97
)

[1] => Array


(
[id_rekening] => 1
[nama_rekening] => Pajak Hotel
[target] => 13294000000
[realisasi] => 13414756255
[persen] => 100.91
)

Sedangkan untuk query yang kedua (per triwulan) bentuknya adalah:


Array
(
[0] => Array
(
[id_rekening] => 1
[triwulan] => Triwulan 1
[target] => 3345000000
[realisasi] => 2486377516
[persen] => 74.33
)

[1] => Array


(

Killer Trik Query MySQL 333


[id_rekening] => 1
[triwulan] => Triwulan 2
[target] => 3150000000
[realisasi] => 2530160543
[persen] => 80.32
)

[2] => Array


(
[id_rekening] => 1
[triwulan] => Triwulan 3
[target] => 2725000000
[realisasi] => 3256605372
[persen] => 119.51
)

Dengan demikian, pada query kedua kita perlu ubah indexnya


berdasarkan id_rekening, sehingga hasil query pertama dapat
dengan mudah dihubungkan dengan query kedua. Perhatikan
ilustrasi berikut:

Gambar 15.15. Ilustrasi Hubungan Antar Data Array

Script yang kita gunakan untuk membuat index per id rekening


adalah sebagai berikut:

1. while($row = mysqli_fetch_assoc($query_triwulan)) {
2. $result_triwulan[$row['id_rekening']][]= $row;

334 BAB 15 Table Reporting Dengan PHP dan MySQL


3. }

Pada script diatas, data array per id rekening kita simpan pada
variabel $result_triwulan.

Keseluruhan script untuk menampilkan data adalah sebagai berikut:

1. if (isset($_GET['submit'])) {
2. if ($hasil_query)
3. {
4. $thead = '<tr>
5. <th>No</th>
6. <th>Nama Rekening</th>
7. <th>Target</th>
8. <th>Realisasi</th>
9. <th>Pencpaian(%)</th>
10. </tr>';
11.
12. echo '
13. <div class="table-responsive">
14. <table>
15. <thead>' . $thead . ' </thead>
16. <tbody>';
17.
18. while($row = mysqli_fetch_assoc($query_triwulan)) {
19. $result_triwulan[$row['id_rekening']][]= $row;
20. }
21.
22. $no = 1;
23. while($row = mysqli_fetch_assoc($query_total)) {
24. echo '<tr class="subtotal">
25. <td>' . $no . '</td>
26. <td>' . $row['nama_rekening'] . '</td>
27. <td class="right">' .
28. number_format($row['target'], 0, ',', '.') . '</td>
29. <td class="right">' .
30. number_format($row['realisasi'], 0, ',', '.') . '</td>
31. <td class="right">'.$row['persen'].'</td>
32. </tr>';
33. foreach ($result_triwulan[$row['id_rekening']] as $val) {
34. echo '<tr>
35. <td></td>
36. <td>' . $val['triwulan'] . '</td>
37.

Killer Trik Query MySQL 335


38. <td class="right">' .
39. number_format($val['target'], 0, ',', '.') . '</td>
40. <td class="right">' .
41. number_format($val['realisasi'], 0, ',', '.') . '</td>
42. <td class="right">'.$val['persen'].'</td>
43. </tr>';
44. }
45. $no++;
46. }
47.
48. echo '</tbody>
49. <tfoot>' . $thead . '</tfoot>
50. </table>
51. </div>';
52. }
53. }

Pada query diatas, pertama kita loop hasil query pada $query total
untuk menampilkan data total per rekening, disaat yang sama kita
ambil data per triwulannya berdasarkan index id_rekening
$result_triwulan[$row['id_rekening']], jika id_rekening 1, maka
data yang diambil adalah $result_triwulan[1]

Lebih Lanjut…
Sama seperti Table Reporting III, terkadang kita perlu
menyandingkan data beberapa tahun sekaligus. Mari kita ubah form
yang telah kita buat menjadi seperti berikut

336 BAB 15 Table Reporting Dengan PHP dan MySQL


Gambar 15.16 Tampilan Form

Ketika disubmit dan yang dicentang tahun 2017 dan 2016 maka
bentuk data yang ditampilkan adalah sebagai berikut:

Gambar 15.17 Tampilan Tabel Hasil Form Submit Jika Tahun Yang dipilih
Lebih Dari Satu

Pada gambar diatas terlihat bahwa pada tahun 2017 terdapat kolom
growth yang merupakan pertumbuhan realisasi dari tahun
sebelumnya

Killer Trik Query MySQL 337


Jika yang dicentang hanya satu tahun, data yang ditampilkan sama
seperti kasus sebelumnya, yaitu sebagai berikut:

Gambar 15.18 Tampilan Tabel Hasil Form Submit Jika Tahun Yang dipilih
Hanya Satu

SQL Query…
Sama seperti sebelumnya, untuk query SQL nya kita pisah antara
query untuk total per rekening dan query untuk rekap per
triwulannya

Jika dicentang beberapa tahun, maka query total per rekening nya
adalah sebagai berikut:

1. SELECT id_rekening, nama_rekening


2. , target_2017, realisasi_2017
3. , ROUND(realisasi_2017
4. / target_2017*100, 2) AS persen_2017
5. , ROUND ( (realisasi_2017 - realisasi_2016)

338 BAB 15 Table Reporting Dengan PHP dan MySQL


6. / realisasi_2016 * 100, 2 ) AS growth_2017
7. , target_2016
8. , realisasi_2016
9. , ROUND(realisasi_2016
10. / target_2016*100, 2) AS persen_2016
11. FROM pjk_rekening
12. LEFT JOIN (
13. SELECT id_rekening
14. ,SUM(IF(tahun = 2017, target, NULL)) AS target_2017
15. ,SUM(IF(tahun = 2016, target, NULL)) AS target_2016
16. FROM pjk_target
17. WHERE tahun = 2017 OR tahun = 2016
18. GROUP BY id_rekening
19. ) AS target USING(id_rekening)
20. LEFT JOIN
21. (
22. SELECT id_rekening
23. ,SUM(IF(YEAR(tanggal) = 2017, pemasukan, NULL))
24. AS realisasi_2017
25. ,SUM(IF(YEAR(tanggal) = 2016, pemasukan, NULL))
26. AS realisasi_2016
27. FROM pjk_realisasi
28. WHERE YEAR(tanggal) = 2017 OR YEAR(tanggal) = 2016
29. GROUP BY id_rekening
30. ) AS realisasi USING (id_rekening)
31. ORDER BY realisasi_2017 ASC

Sedangkan jika yang dicentang hanya satu tahun, maka querynya


adalah sebagai berikut:

1. SELECT id_rekening, nama_rekening


2. , target_2017
3. , realisasi_2017
4. , ROUND(realisasi_2017
5. / target_2017*100, 2) AS persen_2017
6. FROM pjk_rekening
7. LEFT JOIN (
8. SELECT id_rekening
9. , SUM(IF(tahun = 2017, target, NULL)) AS target_2017
10. FROM pjk_target
11. WHERE tahun = 2017
12. GROUP BY id_rekening
13. ) AS target USING(id_rekening)
14. LEFT JOIN
15. (

Killer Trik Query MySQL 339


16. SELECT id_rekening
17. , SUM(IF(YEAR(tanggal) = 2017, pemasukan, NULL))
18. AS realisasi_2017
19. FROM pjk_realisasi
20. WHERE YEAR(tanggal) = 2017
21. GROUP BY id_rekening
22. ) AS realisasi USING (id_rekening)
23. ORDER BY realisasi_2017 ASC

Kita perlu membuat dua query karena jika yang dicentang lebih dari
satu tahun kita perlu membuat query untuk growth.

Mungkin anda bertanya tanya, bukankan growth dapat dibuat


menggunakan script PHP? Jawabnya ya, tapi jika kita membuat
nya pada script PHP, maka data tidak dapat diurutkan
berdasarkan growth. Hal ini dapat disamakan dengan opsi
untuk mengurutkan data berdasarkan persen realisasi
(seperti contoh diatas). Jika persen realisasi dibuat
menggunakan script PHP, maka dipastikan data tidak dapat
diurutkan berdasarkan nilai tersebut.

Meskipun pada contoh kali ini tidak ada opsi untuk mengurutkan
data berdasarkan growth, kita tetap menggunakan dua query untuk
memberi gambaran kepada Anda bagaimana penerapan dua query
dalam aplikasi.

Selanjutnya mari kita susun script PHP yang mengakomodasi kedua


bentuk query diatas.

Script pertama kita buat query total per rekening. Ccript PHP nya
adalah sebagai berikut:

1. $sql_total = '
2. SELECT id_rekening, nama_rekening';
3. arsort($used_year);
4. $keys = array_keys($used_year);

340 BAB 15 Table Reporting Dengan PHP dan MySQL


5. foreach ($used_year as $key => $year) {
6. $sql_total .= ' , target_'. $year .'
7. , realisasi_'. $year .'
8. , ROUND(realisasi_'. $year .'
9. / target_'. $year .'*100, 2) AS persen_'. $year;
10. if ($key < max($keys)) {
11. $sql_total .= ', ROUND ( (realisasi_'. $year .' -
12. realisasi_' . $used_year[$key + 1] . ') / realisasi_' .
13. $used_year[$key + 1] . ' * 100, 2 ) AS growth_'. $year .'';
14. }
15. }
16.
17. $sql_total .= ' FROM pjk_rekening
18. LEFT JOIN (
19. SELECT id_rekening';
20. foreach ($used_year as $key => $year) {
21. $sql_total .= ' , SUM(IF(tahun = '.$year.', target,
22. NULL)) AS target_' . $year;
23. $where_sub[] = ' tahun = ' . $year;
24. }
25. $sql_total .= ' FROM pjk_target WHERE ' . join
26. ($where_sub, ' OR ') . ' GROUP BY id_rekening';
27.
28. $sql_total .= '
29. ) AS target USING(id_rekening)
30. LEFT JOIN
31. (
32. SELECT id_rekening';
33. foreach ($used_year as $key => $year) {
34. $sql_total .= ', SUM(IF(YEAR(tanggal) = '.$year.',
35. pemasukan, NULL)) AS realisasi_' . $year;
36. $where_realisasi[] = ' YEAR(tanggal) = ' . $year;
37. }
38.
39. $sql_total .= ' FROM pjk_realisasi WHERE ' . join
40. ($where_realisasi, ' OR ') . ' GROUP BY id_rekening';
41.
42. $sql_total .= '
43. ) AS realisasi USING (id_rekening)
44. WHERE 1=1 ' . $where_rekening . '
45. ORDER BY ' . $_GET['order_by'] . ' ' . $_GET['sort'];

Pada script diatas, sql untuk total per rekening kita simpan pada
variabel $sql_total.

Killer Trik Query MySQL 341


Selanjutnya kita buat script php untuk menyusun query kedua yaitu
rekap per triwulan. Script PHP yang kita gunakan adalah sebagai
berikut:

1. $sql_triwulan = '
2. SELECT id_rekening
3. , CONCAT("Triwulan ", triwulan) AS triwulan';
4.
5. arsort($used_year);
6. $keys = array_keys($used_year);
7. foreach ($used_year as $key => $year) {
8. $sql_triwulan .= ', target_'. $year .'
9. , realisasi_'. $year .'
10. , ROUND(realisasi_'. $year .'
11. / target_'. $year .'*100, 2) AS
12. persen_'. $year;
13. if ($key < max($keys)) {
14. $sql_triwulan .= ', ROUND ( (realisasi_'.
15. $year .' - realisasi_' . $used_year[$key + 1] . ')
16. / realisasi_' . $used_year[$key + 1] .
17. ' * 100, 2 ) AS growth_'. $year .'';
18.
19. }
20. }
21.
22. $sql_triwulan .= ' FROM pjk_rekening
23. LEFT JOIN (
24. SELECT id_rekening, triwulan';
25. foreach ($used_year as $key => $year) {
26. $sql_triwulan .= ' , SUM(IF(tahun =
27. '.$year.', target, NULL)) AS target_' . $year;
28. $where_sub[] = ' tahun = ' . $year;
29. }
30. $sql_triwulan .= ' FROM pjk_target WHERE ' . join
31. ($where_sub, ' OR ') . ' GROUP BY id_rekening, triwulan';
32.
33. $sql_triwulan .= '
34. ) AS target USING(id_rekening)
35. LEFT JOIN
36. (
37. SELECT id_rekening,
38. CASE WHEN MONTH(tanggal) >= "01"
39. AND MONTH(tanggal) <= "03"
40. THEN 1
41. WHEN MONTH(tanggal) >= "04"

342 BAB 15 Table Reporting Dengan PHP dan MySQL


42. AND MONTH(tanggal) <= "06"
43. THEN 2
44. WHEN MONTH(tanggal) >= "07"
45. AND MONTH(tanggal) <= "09"
46. THEN 3
47. WHEN MONTH(tanggal) >= "10"
48. AND MONTH(tanggal) <= "12"
49. THEN 4
50. END AS triwulan';
51. foreach ($used_year as $key => $year) {
52. $sql_triwulan .= ',SUM(IF(YEAR(tanggal) =
53. '.$year.', pemasukan, NULL)) AS realisasi_' . $year;
54. }
55. $sql_triwulan .= '
56. FROM pjk_realisasi
57. GROUP BY id_rekening,
58. CASE WHEN MONTH(tanggal) >= "01"
59. AND MONTH(tanggal) <= "03"
60. THEN 1
61. WHEN MONTH(tanggal) >= "04"
62. AND MONTH(tanggal) <= "06"
63. THEN 2
64. WHEN MONTH(tanggal) >= "07"
65. AND MONTH(tanggal) <= "09"
66. THEN 3
67. WHEN MONTH(tanggal) >= "10"
68. AND MONTH(tanggal) <= "12"
69. THEN 4
70. END
71. ) AS realisasi USING (id_rekening, triwulan)
72. WHERE 1=1 ' . $where_rekening;

Pada script diatas, sql untuk total per triwulan kita simpan pada
variabel $sql_triwulan

Menampilkan Data…
Selanjutnya kita buat script PHP untuk menampilkan data. Sama
Seperti SQL, tampilan data juga berubah sesuai dengan banyaknya
tahun yang dipilih. Jika tahun yang dipilih lebih dari satu, maka akan
ditampilkan kolom growth pada tahun yang lebih tinggi, jika tahun
yang dipilih hanya satu, maka kolom tersebut tidak ditampilkan.

Killer Trik Query MySQL 343


Script PHP yang kita gunakan adalah sebagai berikut:

1. if (isset($_GET['submit'])) {
2. if ($hasil_query)
3. {
4. $thead = '<tr>
5. <th rowspan="2">No</th>
6. <th rowspan="2">Rekening</th>';
7.
8. foreach ($used_year as $key => $year) {
9. $colspan = $key < max($keys) ? ' colspan="5"' : '
10. colspan="4"';
11. $thead .= '<th'.$colspan.'>'. $year .'</th>';
12. }
13.
14. $thead .= '</tr>';
15.
16. foreach ($used_year as $key => $year) {
17. $thead .= '<th>Target</th>
18. <th>Realisasi</th>
19. <th>Pencapaian(%)</th>';
20. if ($key < max($keys)) {
21. $thead .= '<th>Growth(%)</th>';
22. }
23. }
24.
25. echo '
26. <div class="table-responsive">
27. <table>
28. <thead>' . $thead . ' </thead>
29. <tbody>';
30.
31. while($row = mysqli_fetch_assoc($query_triwulan)) {
32. $result_triwulan[$row['id_rekening']][]= $row;
33. }
34.
35. $no = 1;
36. while($row = mysqli_fetch_assoc($query_total)) {
37.
38. echo '<tr class="subtotal">
39. <td>' . $no . '</td>
40. <td>' . $row['nama_rekening'] . '</td>';
41.
42. foreach ($used_year as $key => $year) {
43. echo'<td class="right">' .
44. number_format($row['target_'.$year], 0, ',', '.') . '</td>
45.

344 BAB 15 Table Reporting Dengan PHP dan MySQL


46. <td class="right">' .
47. number_format($row['realisasi_'.$year], 0, ',', '.') . '</td>
48. <td class="right">' .
49. $row['persen_'.$year] . '</td>';
50.
51. if ($key < max($keys)) {
52. echo '<td class="right">' .
53. $row['growth_'.$year] . '</td>';
54. }
55. }
56. echo '</tr>';
57. foreach ($result_triwulan[$row['id_rekening']] as
58. $val){
59. echo '<tr>
60. <td></td>
61. <td>' . $val['triwulan'] . '</td>';
62. foreach ($used_year as $key => $year) {
63. echo '
64. <td class="right">' .
65. number_format($val['target_'.$year], 0, ',', '.') . '</td>
66. <td class="right">' .
67. number_format($val['realisasi_'.$year], 0, ',', '.') . '</td>
68. <td class="right">' .
69. $val['persen_'.$year] . '</td>';
70. if ($key < max($keys)) {
71. echo '<td class="right">' .
72. $val['growth_'.$year] . '</td>';
73. }
74. }
75. echo '</tr>';
76. }
77. $no++;
78. }
79.
80. echo '</tbody>
81. <tfoot>' . $thead . '</tfoot>
82. </table>
83. </div>';
84. }
85. }

Script diatas mirip seperti script sebelumnya, dimana pada rekap


per triwulan, kita buat array dengan index per id rekening, hasilnya
kita simpan pada variabel $result_triwulan (baris 32).

Killer Trik Query MySQL 345


Untuk menampilkan data, pertama tama kita loop hasil query total
per rekening (yang telah diurutkan datanya dengan query SQL) (baris
36). Selanjutnya didalam loop tersebut, kita ambil data rekap per
triwulan sesuai dengan id rekening yang ada pada tiap tiap baris
pada query total per rekening (baris 56).

15.5. Lebih Lanjut…


Pada bab ini kita telah membahas berbagai bentuk tabel reporting.
Dari pembahasan ini, kita memperoleh pembelajaran bahwa kita
tidak dapat sepenuhnya mengandalkan SQL untuk memperoleh
bentuk tabel output yang diinginkan, karena banyak opsi yang perlu
disediakan kepada user, sehingga bentuk tabel reporting sesuai
dengan kebutuhan

Dilapangan, bentuk tabel ini sangat sangat-sangat bervariasi dan


bisa jadi kompleks dan tidak mungkin semuanya disajikan pada buku
ini, dari pembahasan pada buku ini, yang terpenting adalah bahwa
Anda paham konsep bagaimana menyelesaikan berbagai persolah
tabel reporting, sehingga dapat digunakan sebagai ide pemecahan
masalah tabel reporting yang Anda hadapi

Sejauh pengalaman penulis yang bertahun tahun menghadapi


bentuk penyajian data, betapapun kompleksnya tabel reporting,
semuanya dapat diselesaikan dengan bantuan script dari sisi
aplikasi, so... yakinlah bahwa semua ada solusinya.

346 BAB 15 Table Reporting Dengan PHP dan MySQL


Next Step
Buku ini telah membahas bagaimana peran script aplikasi untuk
menampilkan berbagai bentuk tabel output. Dengan bantuan script
aplikasi, kita dengan mudah dapat menyajikan tabel output sesuai
dengan yang diharapkan.

Namun demikian, sebelum menggunakan bantuan script aplikasi,


penting untuk selalu dipengang bahwa, usahakan semaksimal
mungkin semua permasalahan selesai dengan SQL dan sebisa
mungkin script aplikasi digunakan hanya untuk menampilkan data
saja.

Kenapa?

Karena hal ini akan meningkatkan performa baik dari sisi database
maupun aplikasi.

Untuk dapat menguasai SQL dengan maksimal, anda dapat


membaca buku yang saya tulis: Panduan Query MySQL: Tutotial dan
Referensi Lengkap SQL pada MySQL

Killer Trik Query MySQL 347

Anda mungkin juga menyukai