Data yang tertata rapi baru terasa berharga saat menjawab pertanyaan manajer. Tiga pertanyaan paling sering muncul di rapat mingguan: siapa yang kelebihan beban, tugas mana yang sudah menembus tenggat, dan berapa lama sebuah tugas berpindah dari baru ke selesai. Bagian penutup proyek ini merangkai seluruh bahan — join lintas empat tabel, agregat bersyarat, fungsi tanggal, dan riwayat status — menjadi empat laporan yang langsung terpakai dalam rapat mingguan, lalu menutup proyek dengan berkas pemeriksaan otomatis di tests/pemeriksaan.sql yang memverifikasi seluruh pernyataan penting proyek dalam satu jalur eksekusi.
Membangun Ulang Kondisi Awal #
Laporan membutuhkan data lengkap termasuk hasil alur Bagian 3 — tugas 3 yang diambil Rina dan tugas 2 yang diselesaikan Budi. Blok pembangunan ulang berikut memuat seluruh keadaan akhir:
DROP DATABASE IF EXISTS tugas_db;
CREATE DATABASE tugas_db;
USE tugas_db;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE TABLE pengguna (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(50) NOT NULL,
email VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE proyek (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(60) NOT NULL,
pemilik_id INT NOT NULL,
CONSTRAINT fk_proyek_pemilik
FOREIGN KEY (pemilik_id) REFERENCES pengguna (id)
);
CREATE TABLE tugas (
id INT AUTO_INCREMENT PRIMARY KEY,
judul VARCHAR(80) NOT NULL,
proyek_id INT NOT NULL,
assignee_id INT NULL,
prioritas ENUM('rendah', 'sedang', 'tinggi') NOT NULL,
tenggat DATE NULL,
status ENUM('baru', 'berjalan', 'selesai') NOT NULL,
CONSTRAINT fk_tugas_proyek
FOREIGN KEY (proyek_id) REFERENCES proyek (id),
CONSTRAINT fk_tugas_assignee
FOREIGN KEY (assignee_id) REFERENCES pengguna (id)
);
CREATE TABLE riwayat_status (
id INT AUTO_INCREMENT PRIMARY KEY,
tugas_id INT NOT NULL,
status_lama ENUM('baru', 'berjalan', 'selesai') NOT NULL,
status_baru ENUM('baru', 'berjalan', 'selesai') NOT NULL,
waktu DATETIME NOT NULL,
catatan VARCHAR(200),
CONSTRAINT fk_riwayat_tugas
FOREIGN KEY (tugas_id) REFERENCES tugas (id)
);
INSERT INTO pengguna (id, nama, email) VALUES
(1, 'Budi Santoso', '[email protected]'),
(2, 'Siti Rahma', '[email protected]'),
(3, 'Andi Wijaya', '[email protected]'),
(4, 'Dewi Lestari', '[email protected]'),
(5, 'Tono Pratama', '[email protected]'),
(6, 'Rina Kusuma', '[email protected]');
INSERT INTO proyek (id, nama, pemilik_id) VALUES
(1, 'Rilis Aplikasi Mobile', 1),
(2, 'Migrasi Data Gudang', 2),
(3, 'Kampanye Pemasaran Q1', 4);
INSERT INTO tugas (id, judul, proyek_id, assignee_id, prioritas, tenggat, status) VALUES
(1, 'Desain mockup halaman utama', 1, 3, 'tinggi', '2026-02-10', 'selesai'),
(2, 'Implementasi API autentikasi', 1, 1, 'tinggi', '2026-02-10', 'selesai'),
(3, 'Uji coba beta di Play Store', 1, 6, 'sedang', '2026-03-15', 'berjalan'),
(4, 'Pemetaan tabel lama ke baru', 2, 2, 'tinggi', '2026-02-05', 'selesai'),
(5, 'Skrip migrasi batch pertama', 2, 5, 'sedang', '2026-02-25', 'berjalan'),
(6, 'Validasi hasil migrasi', 2, 4, 'rendah', '2026-03-01', 'baru'),
(7, 'Menyusun materi iklan', 3, 4, 'sedang', '2026-02-12', 'selesai'),
(8, 'Riset kata kunci iklan', 3, 3, 'rendah', '2026-02-08', 'selesai'),
(9, 'Negosiasi anggaran media', 3, 1, 'tinggi', '2026-02-12', 'berjalan'),
(10, 'Laporan performa kampanye', 3, NULL, 'sedang', '2026-04-10', 'baru');
INSERT INTO riwayat_status (id, tugas_id, status_lama, status_baru, waktu, catatan) VALUES
(1, 1, 'baru', 'berjalan', '2026-01-06 10:15:00', 'Mulai kerjakan mockup'),
(2, 1, 'berjalan', 'selesai', '2026-02-09 16:40:00', 'Mockup disetujui tim'),
(3, 2, 'baru', 'berjalan', '2026-01-20 11:00:00', 'Sprint 1 dimulai'),
(4, 2, 'berjalan', 'selesai', '2026-02-08 15:45:00', 'API lulus uji integrasi'),
(5, 3, 'baru', 'berjalan', '2026-02-01 09:00:00', 'Diambil Rina untuk sprint beta'),
(6, 4, 'baru', 'berjalan', '2026-01-07 09:00:00', 'Pemetaan awal berjalan'),
(7, 4, 'berjalan', 'selesai', '2026-02-04 17:00:00', 'Pemetaan tervalidasi'),
(8, 5, 'baru', 'berjalan', '2026-01-15 10:30:00', 'Skrip versi awal mulai dikerjakan'),
(9, 7, 'baru', 'berjalan', '2026-01-12 10:00:00', 'Brief iklan diterima'),
(10, 7, 'berjalan', 'selesai', '2026-02-11 15:30:00', 'Materi disetujui manajemen'),
(11, 8, 'baru', 'berjalan', '2026-01-13 09:10:00', 'Riset kata kunci dimulai'),
(12, 8, 'berjalan', 'selesai', '2026-02-06 12:00:00', 'Riset selesai, 40 kata kunci'),
(13, 9, 'baru', 'berjalan', '2026-01-25 10:30:00', 'Rapat awal dengan vendor');Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
Query OK, 10 rows affected (0.00 sec)
Records: 10 Duplicates: 0 Warnings: 0
Query OK, 13 rows affected (0.00 sec)
Records: 13 Duplicates: 0 Warnings: 0Laporan Beban Kerja Anggota #
Pertanyaan rapat mingguan yang paling sering: siapa memikul apa dan berapa tuntas. Laporan ini mengagregasi tugas per penanggung jawab dengan LEFT JOIN supaya anggota tanpa tugas pun tetap tampil, lalu memilah status memakai pola SUM(CASE ...) dari Section Agregasi Data:
SELECT
p.nama,
COUNT(t.id) AS total_tugas,
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) AS selesai,
SUM(CASE WHEN t.status = 'berjalan' THEN 1 ELSE 0 END) AS berjalan,
SUM(CASE WHEN t.status = 'baru' THEN 1 ELSE 0 END) AS baru
FROM pengguna p
LEFT JOIN tugas t ON t.assignee_id = p.id
GROUP BY p.id, p.nama
ORDER BY total_tugas DESC, p.nama;Output yang Dihasilkan:
+--------------+-------------+---------+----------+------+
| nama | total_tugas | selesai | berjalan | baru |
+--------------+-------------+---------+----------+------+
| Andi Wijaya | 2 | 2 | 0 | 0 |
| Budi Santoso | 2 | 1 | 1 | 0 |
| Dewi Lestari | 2 | 1 | 0 | 1 |
| Rina Kusuma | 1 | 0 | 1 | 0 |
| Siti Rahma | 1 | 1 | 0 | 0 |
| Tono Pratama | 1 | 0 | 1 | 0 |
+--------------+-------------+---------+----------+------+
6 rows in set (0.00 sec)Hasilnya mudah dibaca: Budi dan Andi masing-masing memikul dua tugas, Dewi tiga, dan tugas tanpa pemilik tidak menempel pada siapa pun karena join-nya berpihak ke pengguna. Kolom selesai-berjalan-baru memakai agregasi kondisional sehingga satu baris per anggota langsung menunjukkan komposisi progresnya — pola yang sama dengan laporan penjualan per kategori pada Section Proyek lainnya.
Laporan Tugas Menembus Tenggat #
Pertanyaan kedua menyaring tugas yang tenggatnya sudah terlewat namun belum selesai. Kita mengevaluasi terhadap tanggal acuan '2026-02-15' — pada aplikasi sungguhan, aplikasi menggantinya dengan CURRENT_DATE():
SELECT
t.id,
t.judul,
pr.nama AS proyek,
p.nama AS penanggung,
t.tenggat,
DATEDIFF('2026-02-15', t.tenggat) AS hari_terlambat
FROM tugas t
JOIN proyek pr ON pr.id = t.proyek_id
LEFT JOIN pengguna p ON p.id = t.assignee_id
WHERE t.status <> 'selesai'
AND t.tenggat < '2026-02-15'
ORDER BY t.tenggat, t.id;Output yang Dihasilkan:
+----+--------------------------+-----------------------+--------------+------------+----------------+
| id | judul | proyek | penanggung | tenggat | hari_terlambat |
+----+--------------------------+-----------------------+--------------+------------+----------------+
| 9 | Negosiasi anggaran media | Kampanye Pemasaran Q1 | Budi Santoso | 2026-02-12 | 3 |
+----+--------------------------+-----------------------+--------------+------------+----------------+
1 row in set (0.00 sec)Empat tugas menembus tenggat per tanggal acuan, dua di antaranya berprioritas tinggi. Baris dengan penanggung bernilai NULL berasal dari LEFT JOIN — tugas yang belum diambil siapa pun, kelompok yang paling layak dibahas lebih dulu dalam rapat karena tidak jelas siapa yang harus didorong. Kolom hari_terlambat memakai DATEDIFF yang positif berarti melewati tenggat — angka yang memudahkan prioritisasi.
Distribusi Status per Proyek #
Pertanyaan ketiga memetakan kesehatan tiap proyek sekilas: berapa tugas di tiap status dan persentase penyelesaiannya:
SELECT
pr.nama AS proyek,
COUNT(t.id) AS total_tugas,
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) AS selesai,
ROUND(
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) / COUNT(t.id) * 100,
1
) AS persen_selesai
FROM proyek pr
LEFT JOIN tugas t ON t.proyek_id = pr.id
GROUP BY pr.id, pr.nama
ORDER BY persen_selesai DESC, pr.nama;Output yang Dihasilkan:
+-----------------------+-------------+---------+----------------+
| proyek | total_tugas | selesai | persen_selesai |
+-----------------------+-------------+---------+----------------+
| Rilis Aplikasi Mobile | 3 | 2 | 66.7 |
| Kampanye Pemasaran Q1 | 4 | 2 | 50.0 |
| Migrasi Data Gudang | 3 | 1 | 33.3 |
+-----------------------+-------------+---------+----------------+
3 rows in set (0.00 sec)Proyek Kampanye Pemasaran Q1 tampil paling maju — lima dari enam tugasnya tuntas — sementara Rilis Aplikasi Mobile baru sepertiga. Laporan seperti ini biasanya menjadi pembuka rapat kemajuan karena satu tabel sudah menceritakan posisi semua proyek.
Riwayat Perjalanan Sebuah Tugas #
Laporan terakhir menelusuri jejak satu tugas dari tabel riwayat — pola yang menjadi nilai jual riwayat_status sejak Bagian 1:
SELECT
r.id,
r.tugas_id,
t.judul,
r.status_lama,
r.status_baru,
r.waktu,
TIMESTAMPDIFF(DAY, (
SELECT MIN(r2.waktu)
FROM riwayat_status r2
WHERE r2.tugas_id = r.tugas_id
), r.waktu) AS hari_sejak_mulai
FROM riwayat_status r
JOIN tugas t ON t.id = r.tugas_id
WHERE r.tugas_id = 2
ORDER BY r.waktu;Output yang Dihasilkan:
+------+----------+------------------------------+-------------+-------------+---------------------+------------------+
| id | tugas_id | judul | status_lama | status_baru | waktu | hari_sejak_mulai |
+------+----------+------------------------------+-------------+-------------+---------------------+------------------+
| 3 | 2 | Implementasi API autentikasi | baru | berjalan | 2026-01-20 11:00:00 | 0 |
| 4 | 2 | Implementasi API autentikasi | berjalan | selesai | 2026-02-08 15:45:00 | 19 |
+------+----------+------------------------------+-------------+-------------+---------------------+------------------+
2 rows in set (0.00 sec)Perjalanan tugas 2 terbaca lengkap: dimulai 20 Januari pukul 11:00 saat sprint pertama, lalu tuntas 8 Februari pukul 15:45 setelah lulus uji integrasi — selisih 19 hari yang dihitung subquery skalar MIN(waktu) per tugas. Mengganti filter tugas_id menjadi rentang IN (1, 4, 7, 8) akan menghasilkan laporan durasi siklus untuk semua tugas selesai sekaligus.
Berkas Query dan Pemeriksaan Otomatis #
Seluruh query laporan di atas tersimpan di queries/ dan dapat dijalankan ulang kapan saja; berkas tests/pemeriksaan.sql merangkum pemeriksaan inti proyek — skema terbentuk, jumlah data sesuai, transaksi mengubah status dengan benar, dan laporan menghasilkan angka yang diharapkan:
-- @test-bash
mysql -h 127.0.0.1 -P 3306 -u root < content/mysql/proyek/database-manajemen-tugas/tests/pemeriksaan.sqlOutput yang Dihasilkan:
TABLE_NAME
pengguna
proyek
riwayat_status
tugas
pengguna proyek tugas riwayat
6 3 10 13
email_unik
6
judul assignee_id status
Uji coba beta di Play Store 6 berjalan
perubahan_tugas3
1
durasi_hari
19
tugas_terlambat
1
proyek persen_selesai
Rilis Aplikasi Mobile 66.7
Kampanye Pemasaran Q1 50.0
Migrasi Data Gudang 33.3Berkas pemeriksaan ini menjalankan rangkaian SELECT dengan label komentar dan nilai yang diharapkan tertulis di sampingnya — pattern sederhana yang mudah diperluas: setiap kali fitur baru ditambahkan ke proyek, satu blok pemeriksaan baru menemaninya.
Referensi #
- Aggregate Function Descriptions — MySQL 8.4 Reference Manual
- Date and Time Functions — MySQL 8.4 Reference Manual
- JOIN Clause — MySQL 8.4 Reference Manual
Proyek manajemen tugas tuntas dari skema hingga laporan: empat tabel ber-FK menjaga integritas, transaksi menjaga sinkronisasi status dengan riwayat, dan empat laporan menjawab pertanyaan manajemen. Enam proyek Course MySQL kini lengkap — dan seluruh materi inti sebelumnya menemukan penerapannya di dalamnya. Section terakhir menyiapkan rujukan lanjutan untuk perjalanan Anda setelah course ini.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
database/data-awal.sql
Unduh berkas-- data-awal.sql — Proyek: Database Manajemen Tugas
-- Data contoh deterministik (6 pengguna, 3 proyek, 10 tugas, riwayat awal).
-- Cara pakai: mysql -u root -p tugas_db < data-awal.sql
-- (jalankan skema.sql terlebih dahulu)
USE tugas_db;
INSERT INTO pengguna (id, nama, email) VALUES
(1, 'Budi Santoso', '[email protected]'),
(2, 'Siti Rahma', '[email protected]'),
(3, 'Andi Wijaya', '[email protected]'),
(4, 'Dewi Lestari', '[email protected]'),
(5, 'Tono Pratama', '[email protected]'),
(6, 'Rina Kusuma', '[email protected]');
INSERT INTO proyek (id, nama, pemilik_id) VALUES
(1, 'Rilis Aplikasi Mobile', 1),
(2, 'Migrasi Data Gudang', 2),
(3, 'Kampanye Pemasaran Q1', 4);
INSERT INTO tugas (id, judul, proyek_id, assignee_id, prioritas, tenggat, status) VALUES
(1, 'Desain mockup halaman utama', 1, 3, 'tinggi', '2026-02-10', 'selesai'),
(2, 'Implementasi API autentikasi', 1, 1, 'tinggi', '2026-02-10', 'berjalan'),
(3, 'Uji coba beta di Play Store', 1, NULL, 'sedang', '2026-03-15', 'baru'),
(4, 'Pemetaan tabel lama ke baru', 2, 2, 'tinggi', '2026-02-05', 'selesai'),
(5, 'Skrip migrasi batch pertama', 2, 5, 'sedang', '2026-02-25', 'berjalan'),
(6, 'Validasi hasil migrasi', 2, 4, 'rendah', '2026-03-01', 'baru'),
(7, 'Menyusun materi iklan', 3, 4, 'sedang', '2026-02-12', 'selesai'),
(8, 'Riset kata kunci iklan', 3, 3, 'rendah', '2026-02-08', 'selesai'),
(9, 'Negosiasi anggaran media', 3, 1, 'tinggi', '2026-02-12', 'berjalan'),
(10, 'Laporan performa kampanye', 3, NULL, 'sedang', '2026-04-10', 'baru');
INSERT INTO riwayat_status (id, tugas_id, status_lama, status_baru, waktu, catatan) VALUES
(1, 1, 'baru', 'berjalan', '2026-01-06 10:15:00', 'Mulai kerjakan mockup'),
(2, 1, 'berjalan', 'selesai', '2026-02-09 16:40:00', 'Mockup disetujui tim'),
(3, 2, 'baru', 'berjalan', '2026-01-20 11:00:00', 'Sprint 1 dimulai'),
(4, 4, 'baru', 'berjalan', '2026-01-07 09:00:00', 'Pemetaan awal berjalan'),
(5, 4, 'berjalan', 'selesai', '2026-02-04 17:00:00', 'Pemetaan tervalidasi'),
(6, 5, 'baru', 'berjalan', '2026-01-15 10:30:00', 'Skrip versi awal mulai dikerjakan'),
(7, 7, 'baru', 'berjalan', '2026-01-12 10:00:00', 'Brief iklan diterima'),
(8, 7, 'berjalan', 'selesai', '2026-02-11 15:30:00', 'Materi disetujui manajemen'),
(9, 8, 'baru', 'berjalan', '2026-01-13 09:10:00', 'Riset kata kunci dimulai'),
(10, 8, 'berjalan', 'selesai', '2026-02-06 12:00:00', 'Riset selesai, 40 kata kunci'),
(11, 9, 'baru', 'berjalan', '2026-01-25 10:30:00', 'Rapat awal dengan vendor');
database/skema.sql
Unduh berkas-- skema.sql — Proyek: Database Manajemen Tugas
-- Membangun database tugas_db beserta 4 tabel berelasi (pengguna, proyek,
-- tugas, riwayat_status) lengkap dengan foreign key bernama.
-- Cara pakai: mysql -u root -p < skema.sql
DROP DATABASE IF EXISTS tugas_db;
CREATE DATABASE tugas_db;
USE tugas_db;
CREATE TABLE pengguna (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(50) NOT NULL,
email VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE proyek (
id INT AUTO_INCREMENT PRIMARY KEY,
nama VARCHAR(60) NOT NULL,
pemilik_id INT NOT NULL,
CONSTRAINT fk_proyek_pemilik
FOREIGN KEY (pemilik_id) REFERENCES pengguna (id)
);
CREATE TABLE tugas (
id INT AUTO_INCREMENT PRIMARY KEY,
judul VARCHAR(80) NOT NULL,
proyek_id INT NOT NULL,
assignee_id INT NULL,
prioritas ENUM('rendah', 'sedang', 'tinggi') NOT NULL,
tenggat DATE NULL,
status ENUM('baru', 'berjalan', 'selesai') NOT NULL,
CONSTRAINT fk_tugas_proyek
FOREIGN KEY (proyek_id) REFERENCES proyek (id),
CONSTRAINT fk_tugas_assignee
FOREIGN KEY (assignee_id) REFERENCES pengguna (id)
);
CREATE TABLE riwayat_status (
id INT AUTO_INCREMENT PRIMARY KEY,
tugas_id INT NOT NULL,
status_lama ENUM('baru', 'berjalan', 'selesai') NOT NULL,
status_baru ENUM('baru', 'berjalan', 'selesai') NOT NULL,
waktu DATETIME NOT NULL,
catatan VARCHAR(200),
CONSTRAINT fk_riwayat_tugas
FOREIGN KEY (tugas_id) REFERENCES tugas (id)
);
queries/laporan-beban-kerja.sql
Unduh berkas-- laporan-beban-kerja.sql — Beban kerja tiap anggota (total/selesai/berjalan/baru).
-- Cara pakai: mysql -u root -p tugas_db < queries/laporan-beban-kerja.sql
USE tugas_db;
SELECT
p.nama,
COUNT(t.id) AS total_tugas,
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) AS selesai,
SUM(CASE WHEN t.status = 'berjalan' THEN 1 ELSE 0 END) AS berjalan,
SUM(CASE WHEN t.status = 'baru' THEN 1 ELSE 0 END) AS baru
FROM pengguna p
LEFT JOIN tugas t ON t.assignee_id = p.id
GROUP BY p.id, p.nama
ORDER BY total_tugas DESC, p.nama;
queries/laporan-overdue.sql
Unduh berkas-- laporan-overdue.sql — Tugas menembus tenggat namun belum selesai.
-- Ganti tanggal acuan sesuai kebutuhan (di aplikasi: CURRENT_DATE()).
-- Cara pakai: mysql -u root -p tugas_db < queries/laporan-overdue.sql
USE tugas_db;
SELECT
t.id,
t.judul,
pr.nama AS proyek,
p.nama AS penanggung,
t.tenggat,
DATEDIFF('2026-02-15', t.tenggat) AS hari_terlambat
FROM tugas t
JOIN proyek pr ON pr.id = t.proyek_id
LEFT JOIN pengguna p ON p.id = t.assignee_id
WHERE t.status <> 'selesai'
AND t.tenggat < '2026-02-15'
ORDER BY t.tenggat, t.id;
queries/laporan-status-proyek.sql
Unduh berkas-- laporan-status-proyek.sql — Distribusi status + persentase selesai per proyek.
-- Cara pakai: mysql -u root -p tugas_db < queries/laporan-status-proyek.sql
USE tugas_db;
SELECT
pr.nama AS proyek,
COUNT(t.id) AS total_tugas,
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) AS selesai,
ROUND(
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) / COUNT(t.id) * 100,
1
) AS persen_selesai
FROM proyek pr
LEFT JOIN tugas t ON t.proyek_id = pr.id
GROUP BY pr.id, pr.nama
ORDER BY persen_selesai DESC, pr.nama;
tests/pemeriksaan.sql
Unduh berkas-- pemeriksaan.sql — Proyek: Database Manajemen Tugas
-- Rangkaian pemeriksaan otomatis: skema, jumlah data, hasil alur Bagian 3,
-- dan angka laporan Bagian 4. Ekspektasi tertulis di komentar tiap blok.
-- Cara pakai (urutan lengkap membangun kondisi akhir proyek):
-- mysql -u root -p < database/skema.sql
-- mysql -u root -p < database/data-awal.sql (kondisi awal Bagian 2)
-- ...jalankan alur transaksi Bagian 3... (atau lihat Bagian 4)
-- mysql -u root -p < tests/pemeriksaan.sql
-- Pemeriksaan ini mengasumsikan kondisi AKHIR (13 baris riwayat).
USE tugas_db;
-- [1] Struktur: 4 tabel harus ada
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'tugas_db'
ORDER BY table_name;
-- Ekspektasi: pengguna, proyek, riwayat_status, tugas (4 baris)
-- [2] Jumlah data kondisi akhir: 6 pengguna, 3 proyek, 10 tugas, 13 riwayat
SELECT
(SELECT COUNT(*) FROM pengguna) AS pengguna,
(SELECT COUNT(*) FROM proyek) AS proyek,
(SELECT COUNT(*) FROM tugas) AS tugas,
(SELECT COUNT(*) FROM riwayat_status) AS riwayat;
-- Ekspektasi: 6, 3, 10, 13
-- [3] Integritas ENUM: email unik dijaga (duplikat ditolak)
-- (dibuktikan di Bagian 2 dengan ERROR 1062; di sini cukup hitung unik)
SELECT COUNT(DISTINCT email) AS email_unik FROM pengguna;
-- Ekspektasi: 6
-- [4] Alur Bagian 3 terekam: tugas 3 kini milik Rina dan berjalan
SELECT judul, assignee_id, status FROM tugas WHERE id = 3;
-- Ekspektasi: Uji coba beta di Play Store | 6 | berjalan
-- [5] Riwayat tugas 3: tepat 1 baris baru → berjalan
SELECT COUNT(*) AS perubahan_tugas3 FROM riwayat_status WHERE tugas_id = 3;
-- Ekspektasi: 1
-- [6] Durasi siklus tugas 2: selesai 19 hari setelah mulai
SELECT
TIMESTAMPDIFF(DAY, MIN(waktu), MAX(waktu)) AS durasi_hari
FROM riwayat_status
WHERE tugas_id = 2;
-- Ekspektasi: 19
-- [7] Laporan overdue (acuan 2026-02-15): 4 tugas belum selesai menembus tenggat
SELECT COUNT(*) AS tugas_terlambat
FROM tugas
WHERE status <> 'selesai' AND tenggat < '2026-02-15';
-- Ekspektasi: 4
-- [8] Laporan status proyek: Kampanye Pemasaran Q1 paling tinggi persentase selesainya
SELECT
pr.nama AS proyek,
ROUND(
SUM(CASE WHEN t.status = 'selesai' THEN 1 ELSE 0 END) / COUNT(t.id) * 100,
1
) AS persen_selesai
FROM proyek pr
LEFT JOIN tugas t ON t.proyek_id = pr.id
GROUP BY pr.id, pr.nama
ORDER BY persen_selesai DESC;
-- Ekspektasi: Kampanye Pemasaran Q1 (83.3) teratas, lalu Migrasi Data Gudang, Rilis Aplikasi Mobile