Skema yang kita rancang di Bagian 1 baru berupa kerangka kosong; kegunaannya baru terasa setelah diisi dan dioperasikan. Pada bagian ini kita memegang peran petugas perpustakaan dalam pekerjaan harinya: mengisi katalog buku, mendata anggota, menambah judul baru, memperbaiki data, sampai menghapus anggota yang keluar. Di sepanjang jalan kita sengaja mencoba memasukkan data yang tidak valid — kode buku ganda dan peminjaman dari anggota yang tidak ada — untuk membuktikan constraint pada skema benar-benar bekerja, bukan sekadar hiasan deklarasi.

Menyiapkan Ulang Database #

Setiap bagian proyek ini mandiri: kita mulai dari database bersih yang dibangun ulang lewat database/skema.sql, sehingga hasil tiap bagian selalu bisa direproduksi tanpa bergantung pada bagian sebelumnya. Blok berikut menjalankan pernyataan yang sama dengan berkas skema dari Bagian 1:

sql
DROP DATABASE IF EXISTS perpustakaan_db;
CREATE DATABASE perpustakaan_db;
USE perpustakaan_db;

CREATE TABLE buku (
  kode_buku VARCHAR(15) PRIMARY KEY,
  judul VARCHAR(100) NOT NULL,
  penulis VARCHAR(80) NOT NULL,
  tahun YEAR NOT NULL,
  stok INT NOT NULL DEFAULT 0,
  CONSTRAINT chk_buku_stok CHECK (stok >= 0)
);

CREATE TABLE anggota (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(60) NOT NULL,
  kelas VARCHAR(10) NOT NULL,
  telepon VARCHAR(15) NULL
);

CREATE TABLE peminjaman (
  id INT AUTO_INCREMENT PRIMARY KEY,
  anggota_id INT NOT NULL,
  tanggal_pinjam DATE NOT NULL,
  tanggal_harus_kembali DATE NOT NULL,
  tanggal_kembali DATE NULL,
  CONSTRAINT fk_peminjaman_anggota
    FOREIGN KEY (anggota_id) REFERENCES anggota(id)
);

CREATE TABLE detail_peminjaman (
  peminjaman_id INT NOT NULL,
  buku_kode VARCHAR(15) NOT NULL,
  jumlah INT NOT NULL,
  PRIMARY KEY (peminjaman_id, buku_kode),
  CONSTRAINT fk_detail_peminjaman
    FOREIGN KEY (peminjaman_id) REFERENCES peminjaman(id),
  CONSTRAINT fk_detail_buku
    FOREIGN KEY (buku_kode) REFERENCES buku(kode_buku)
);

Output yang Dihasilkan:

text
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
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)

Mengisi Data Awal #

Data contoh kita simpan di berkas database/data-awal.sql supaya seluruh bagian berikutnya memakai titik awal yang sama. Delapan judul buku dan delapan anggota siswa semuanya fiktif, dengan nama-nama khas Indonesia:

sql
INSERT INTO buku (kode_buku, judul, penulis, tahun, stok) VALUES
  ('BK-001', 'Laskar Pelangi', 'Andrea Hirata', 2005, 3),
  ('BK-002', 'Bumi Manusia', 'Pramoedya Ananta Toer', 1980, 2),
  ('BK-003', 'Hujan Bulan Juni', 'Sapardi Djoko Damono', 1994, 2),
  ('BK-004', 'Dilan 1990', 'Pidi Baiq', 2014, 4),
  ('BK-005', 'Atomic Habits', 'James Clear', 2018, 2),
  ('BK-006', 'Matematika untuk SMA Kelas X', 'Tim Pusat Kurikulum', 2019, 6),
  ('BK-007', 'Kamus Besar Bahasa Indonesia', 'Badan Pengembangan Bahasa', 2016, 2),
  ('BK-008', 'Fisika Terapan untuk SMK', 'Ratna Wijaya', 2021, 3);

INSERT INTO anggota (nama, kelas, telepon) VALUES
  ('Budi Santoso', 'X-IPA-1', '081234560001'),
  ('Siti Rahmawati', 'X-IPA-2', '081234560002'),
  ('Andi Wijaya', 'XI-IPA-1', '081234560003'),
  ('Dewi Lestari', 'XI-IPS-1', '081234560004'),
  ('Rina Marlina', 'XII-IPA-1', '081234560005'),
  ('Tono Sutrisno', 'XII-IPS-1', NULL),
  ('Sari Puspita', 'X-IPS-1', '081234560007'),
  ('Joko Prasetyo', 'XI-IPS-2', NULL);

Output yang Dihasilkan:

text
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

Dua keputusan kecil pada blok di atas patut dicatat. Pertama, INSERT multi-baris memakai satu pernyataan untuk delapan baris — lebih ringkas dan lebih cepat daripada delapan INSERT terpisah. Kedua, dua anggota sengaja diberi NULL pada kolom telepon; kolom yang boleh kosong tetap perlu contoh datanya supaya perilaku NULL terlihat di query berikutnya.

Membaca Kembali Data #

Isi yang baru ditulis kita baca kembali untuk memastikan apa yang tersimpan memang sesuai dugaan. Kedua query diurutkan eksplisit supaya urutan barisnya pasti:

sql
SELECT kode_buku, judul, penulis, tahun, stok
FROM buku
ORDER BY kode_buku;

Output yang Dihasilkan:

text
+-----------+------------------------------+---------------------------+-------+------+
| kode_buku | judul                        | penulis                   | tahun | stok |
+-----------+------------------------------+---------------------------+-------+------+
| BK-001    | Laskar Pelangi               | Andrea Hirata             |  2005 |    3 |
| BK-002    | Bumi Manusia                 | Pramoedya Ananta Toer     |  1980 |    2 |
| BK-003    | Hujan Bulan Juni             | Sapardi Djoko Damono      |  1994 |    2 |
| BK-004    | Dilan 1990                   | Pidi Baiq                 |  2014 |    4 |
| BK-005    | Atomic Habits                | James Clear               |  2018 |    2 |
| BK-006    | Matematika untuk SMA Kelas X | Tim Pusat Kurikulum       |  2019 |    6 |
| BK-007    | Kamus Besar Bahasa Indonesia | Badan Pengembangan Bahasa |  2016 |    2 |
| BK-008    | Fisika Terapan untuk SMK     | Ratna Wijaya              |  2021 |    3 |
+-----------+------------------------------+---------------------------+-------+------+
8 rows in set (0.00 sec)
sql
SELECT id, nama, kelas, telepon
FROM anggota
ORDER BY id;

Output yang Dihasilkan:

text
+----+----------------+-----------+--------------+
| id | nama           | kelas     | telepon      |
+----+----------------+-----------+--------------+
|  1 | Budi Santoso   | X-IPA-1   | 081234560001 |
|  2 | Siti Rahmawati | X-IPA-2   | 081234560002 |
|  3 | Andi Wijaya    | XI-IPA-1  | 081234560003 |
|  4 | Dewi Lestari   | XI-IPS-1  | 081234560004 |
|  5 | Rina Marlina   | XII-IPA-1 | 081234560005 |
|  6 | Tono Sutrisno  | XII-IPS-1 | NULL         |
|  7 | Sari Puspita   | X-IPS-1   | 081234560007 |
|  8 | Joko Prasetyo  | XI-IPS-2  | NULL         |
+----+----------------+-----------+--------------+
8 rows in set (0.00 sec)

Perhatikan kolom telepon pada hasil kedua: Tono dan Joko tampil sebagai NULL — penanda “tidak ada nilai”, bukan string kosong. Perbedaan ini penting saat memfilter: WHERE telepon = '' tidak akan menemukan keduanya, sedangkan WHERE telepon IS NULL akan.

Menambah dan Mengubah Data #

Kehidupan perpustakaan tidak berhenti di data awal. Judul baru masuk, donasi menambah eksemplar, dan anggota memperbarui nomor telepon. Tiga pernyataan berikut mensimulasikan ketiganya:

sql
INSERT INTO buku (kode_buku, judul, penulis, tahun, stok)
VALUES ('BK-009', 'Negeri 5 Menara', 'Ahmad Fuadi', 2009, 2);

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)
sql
UPDATE buku SET stok = stok + 2 WHERE kode_buku = 'BK-005';

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
sql
UPDATE anggota SET telepon = '081234560006' WHERE id = 6;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Ketiga pernyataan itu masing-masing satu baris hasil: satu buku baru (BK-009), dua eksemplar tambahan untuk Atomic Habits (stok 2 menjadi 4), dan telepon Tono yang semula NULL kini terisi. Perlu diperhatikan bahwa UPDATE kedua memakai ekspresi stok = stok + 2, bukan nilai tetap — pola ini menjaga kebenaran meski kita tidak tahu nilai stok sebelumnya. Perhatikan pula laporan MySQL Rows matched: 1 Changed: 1: satu baris cocok dengan WHERE dan satu baris berubah; dua angka yang sama berarti perubahan menjangkau tepat satu baris.

Sebaliknya, penghapusan layak diperlakukan hati-hati. Joko (id 8) pindah sekolah sehingga keanggotaannya dihapus:

sql
DELETE FROM anggota WHERE id = 8;
SELECT id, nama, kelas FROM anggota ORDER BY id;

Output yang Dihasilkan:

text
Query OK, 1 row affected (0.00 sec)

+----+----------------+-----------+
| id | nama           | kelas     |
+----+----------------+-----------+
|  1 | Budi Santoso   | X-IPA-1   |
|  2 | Siti Rahmawati | X-IPA-2   |
|  3 | Andi Wijaya    | XI-IPA-1  |
|  4 | Dewi Lestari   | XI-IPS-1  |
|  5 | Rina Marlina   | XII-IPA-1 |
|  6 | Tono Sutrisno  | XII-IPS-1 |
|  7 | Sari Puspita   | X-IPS-1   |
+----+----------------+-----------+
7 rows in set (0.00 sec)

Menguji Constraint dengan Data Tidak Valid #

Bagian terpenting bagian ini adalah membuktikan constraint bekerja. Pertama kita coba mendaftarkan buku baru dengan kode yang sudah dipakai — BK-001 sudah dimiliki Laskar Pelangi:

sql
-- @error
INSERT INTO buku (kode_buku, judul, penulis, tahun, stok) VALUES ('BK-001', 'Laskar Pelangi (salinan)', 'Andrea Hirata', 2005, 1);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1062 (23000): Duplicate entry 'BK-001' for key 'buku.PRIMARY'
Bye

MySQL menolak pernyataan itu: Duplicate entry 'BK-001' for key 'buku.PRIMARY'. Primary key menjamin kode buku unik sejak level penyimpanan, sehingga dua judul berbeda tidak mungkin berbagi kode. Kedua, kita coba mencatat peminjaman atas anggota bernomor 99 yang tidak pernah terdaftar:

sql
-- @error
INSERT INTO peminjaman (anggota_id, tanggal_pinjam, tanggal_harus_kembali) VALUES (99, '2026-03-01', '2026-03-08');

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`perpustakaan_db`.`peminjaman`, CONSTRAINT `fk_peminjaman_anggota` FOREIGN KEY (`anggota_id`) REFERENCES `anggota` (`id`))
Bye

Error 1452 kali ini datang dari foreign key fk_peminjaman_anggota: baris anak (peminjaman) menuntut nilai anggota_id yang benar-benar ada di tabel induk (anggota). Dua penolakan ini menegaskan pola penting: constraint adalah penjaga terakhir yang tetap bekerja meski aplikasi punya bug — data tidak valid tidak akan pernah lolos ke penyimpanan.

Referensi #

Katalog dan data anggota kini hidup dan terjaga constraint-nya. Namun perpustakaan yang sejati diukur dari sirkulasi bukunya — pada Bagian 3 kita menjalankan alur inti: peminjaman yang mengurangi stok dan pengembalian yang mengembalikannya, semuanya dalam transaksi yang atomik.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

database/data-awal.sql

Unduh berkas
sql
-- data-awal.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: mengisi data contoh deterministik (buku dan anggota fiktif)
--   sebagai titik awal konsisten untuk seluruh bagian proyek.
-- Cara pakai: jalankan setelah skema.sql:
--   mysql -u root -p perpustakaan_db < database/data-awal.sql

INSERT INTO buku (kode_buku, judul, penulis, tahun, stok) VALUES
  ('BK-001', 'Laskar Pelangi', 'Andrea Hirata', 2005, 3),
  ('BK-002', 'Bumi Manusia', 'Pramoedya Ananta Toer', 1980, 2),
  ('BK-003', 'Hujan Bulan Juni', 'Sapardi Djoko Damono', 1994, 2),
  ('BK-004', 'Dilan 1990', 'Pidi Baiq', 2014, 4),
  ('BK-005', 'Atomic Habits', 'James Clear', 2018, 2),
  ('BK-006', 'Matematika untuk SMA Kelas X', 'Tim Pusat Kurikulum', 2019, 6),
  ('BK-007', 'Kamus Besar Bahasa Indonesia', 'Badan Pengembangan Bahasa', 2016, 2),
  ('BK-008', 'Fisika Terapan untuk SMK', 'Ratna Wijaya', 2021, 3);

INSERT INTO anggota (nama, kelas, telepon) VALUES
  ('Budi Santoso', 'X-IPA-1', '081234560001'),
  ('Siti Rahmawati', 'X-IPA-2', '081234560002'),
  ('Andi Wijaya', 'XI-IPA-1', '081234560003'),
  ('Dewi Lestari', 'XI-IPS-1', '081234560004'),
  ('Rina Marlina', 'XII-IPA-1', '081234560005'),
  ('Tono Sutrisno', 'XII-IPS-1', NULL),
  ('Sari Puspita', 'X-IPS-1', '081234560007'),
  ('Joko Prasetyo', 'XI-IPS-2', NULL);

database/skema.sql

Unduh berkas
sql
-- skema.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: membuat database perpustakaan_db beserta keempat tabelnya
--   (buku, anggota, peminjaman, detail_peminjaman) lengkap dengan
--   primary key, foreign key, dan check constraint.
-- Cara pakai: mysql -u root -p < database/skema.sql

DROP DATABASE IF EXISTS perpustakaan_db;
CREATE DATABASE perpustakaan_db;
USE perpustakaan_db;

-- Katalog buku; kolom stok menyimpan jumlah eksemplar yang tersedia di rak.
CREATE TABLE buku (
  kode_buku VARCHAR(15) PRIMARY KEY,
  judul VARCHAR(100) NOT NULL,
  penulis VARCHAR(80) NOT NULL,
  tahun YEAR NOT NULL,
  stok INT NOT NULL DEFAULT 0,
  CONSTRAINT chk_buku_stok CHECK (stok >= 0)
);

-- Anggota perpustakaan (siswa); telepon boleh kosong (NULL).
CREATE TABLE anggota (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(60) NOT NULL,
  kelas VARCHAR(10) NOT NULL,
  telepon VARCHAR(15) NULL
);

-- Satu baris per transaksi peminjaman; tanggal_kembali NULL berarti masih dipinjam.
CREATE TABLE peminjaman (
  id INT AUTO_INCREMENT PRIMARY KEY,
  anggota_id INT NOT NULL,
  tanggal_pinjam DATE NOT NULL,
  tanggal_harus_kembali DATE NOT NULL,
  tanggal_kembali DATE NULL,
  CONSTRAINT fk_peminjaman_anggota
    FOREIGN KEY (anggota_id) REFERENCES anggota(id)
);

-- Rincian buku per peminjaman; jembatan banyak-ke-banyak antara buku dan peminjaman.
CREATE TABLE detail_peminjaman (
  peminjaman_id INT NOT NULL,
  buku_kode VARCHAR(15) NOT NULL,
  jumlah INT NOT NULL,
  PRIMARY KEY (peminjaman_id, buku_kode),
  CONSTRAINT fk_detail_peminjaman
    FOREIGN KEY (peminjaman_id) REFERENCES peminjaman(id),
  CONSTRAINT fk_detail_buku
    FOREIGN KEY (buku_kode) REFERENCES buku(kode_buku)
);

queries/laporan-anggota-aktif.sql

Unduh berkas
sql
-- laporan-anggota-aktif.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: menampilkan frekuensi peminjaman dan total buku yang dipinjam
--   tiap anggota, termasuk anggota yang belum pernah meminjam.
-- Cara pakai: mysql -u root -p perpustakaan_db < queries/laporan-anggota-aktif.sql

SELECT a.id, a.nama, a.kelas,
       COUNT(DISTINCT p.id) AS jumlah_peminjaman,
       COALESCE(SUM(dp.jumlah), 0) AS total_buku
FROM anggota a
LEFT JOIN peminjaman p ON p.anggota_id = a.id
LEFT JOIN detail_peminjaman dp ON dp.peminjaman_id = p.id
GROUP BY a.id, a.nama, a.kelas
ORDER BY jumlah_peminjaman DESC, a.nama;

queries/laporan-buku-terpopuler.sql

Unduh berkas
sql
-- laporan-buku-terpopuler.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: memeringkat buku berdasarkan total eksemplar yang dipinjam,
--   termasuk buku yang belum pernah dipinjam, memakai window function RANK().
-- Cara pakai: mysql -u root -p perpustakaan_db < queries/laporan-buku-terpopuler.sql

SELECT b.kode_buku, b.judul,
       COALESCE(SUM(dp.jumlah), 0) AS total_dipinjam,
       RANK() OVER (ORDER BY COALESCE(SUM(dp.jumlah), 0) DESC) AS peringkat
FROM buku b
LEFT JOIN detail_peminjaman dp ON dp.buku_kode = b.kode_buku
GROUP BY b.kode_buku, b.judul
ORDER BY total_dipinjam DESC, b.judul;

queries/laporan-terlambat.sql

Unduh berkas
sql
-- laporan-terlambat.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: menemukan peminjaman yang melewati batas tanggal_harus_kembali
--   dan belum dikembalikan, dihitung terhadap tanggal acuan literal.
-- Cara pakai: mysql -u root -p perpustakaan_db < queries/laporan-terlambat.sql
-- Ganti tanggal acuan '2026-02-28' sesuai hari laporan dijalankan.

SELECT p.id, a.nama, a.kelas, p.tanggal_pinjam, p.tanggal_harus_kembali,
       DATEDIFF('2026-02-28', p.tanggal_harus_kembali) AS hari_terlambat
FROM peminjaman p
JOIN anggota a ON a.id = p.anggota_id
WHERE p.tanggal_kembali IS NULL
  AND p.tanggal_harus_kembali < '2026-02-28'
ORDER BY hari_terlambat DESC;

queries/stok-tersedia.sql

Unduh berkas
sql
-- stok-tersedia.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: menampilkan stok tiap judul berdampingan dengan jumlah eksemplar
--   yang sedang dipinjam, sehingga kecukupan koleksi terlihat sekali baca.
-- Cara pakai: mysql -u root -p perpustakaan_db < queries/stok-tersedia.sql

SELECT b.kode_buku, b.judul, b.stok AS stok_tersedia,
       COALESCE(dipinjam.sedang, 0) AS sedang_dipinjam
FROM buku b
LEFT JOIN (
  SELECT dp.buku_kode, SUM(dp.jumlah) AS sedang
  FROM detail_peminjaman dp
  JOIN peminjaman p ON p.id = dp.peminjaman_id
  WHERE p.tanggal_kembali IS NULL
  GROUP BY dp.buku_kode
) dipinjam ON dipinjam.buku_kode = b.kode_buku
ORDER BY b.kode_buku;

tests/pemeriksaan.sql

Unduh berkas
sql
-- pemeriksaan.sql — Proyek Database Manajemen Perpustakaan
-- Tujuan: pemeriksaan akhir seluruh fitur proyek (skema, peminjaman,
--   pengembalian, laporan terpopuler, dan deteksi keterlambatan).
--   Berkas ini MANDIRI: membangun skenario uji kecilnya sendiri,
--   lalu membersihkan database setelah selesai.
-- Cara pakai: mysql -u root -p < tests/pemeriksaan.sql
-- Ekspektasi tiap hasil ditulis sebagai komentar di atas query terkait.

DROP DATABASE IF EXISTS perpustakaan_db;
CREATE DATABASE perpustakaan_db;
USE perpustakaan_db;

CREATE TABLE buku (
  kode_buku VARCHAR(15) PRIMARY KEY,
  judul VARCHAR(100) NOT NULL,
  penulis VARCHAR(80) NOT NULL,
  tahun YEAR NOT NULL,
  stok INT NOT NULL DEFAULT 0,
  CONSTRAINT chk_buku_stok CHECK (stok >= 0)
);
CREATE TABLE anggota (
  id INT AUTO_INCREMENT PRIMARY KEY,
  nama VARCHAR(60) NOT NULL,
  kelas VARCHAR(10) NOT NULL,
  telepon VARCHAR(15) NULL
);
CREATE TABLE peminjaman (
  id INT AUTO_INCREMENT PRIMARY KEY,
  anggota_id INT NOT NULL,
  tanggal_pinjam DATE NOT NULL,
  tanggal_harus_kembali DATE NOT NULL,
  tanggal_kembali DATE NULL,
  CONSTRAINT fk_peminjaman_anggota
    FOREIGN KEY (anggota_id) REFERENCES anggota(id)
);
CREATE TABLE detail_peminjaman (
  peminjaman_id INT NOT NULL,
  buku_kode VARCHAR(15) NOT NULL,
  jumlah INT NOT NULL,
  PRIMARY KEY (peminjaman_id, buku_kode),
  CONSTRAINT fk_detail_peminjaman
    FOREIGN KEY (peminjaman_id) REFERENCES peminjaman(id),
  CONSTRAINT fk_detail_buku
    FOREIGN KEY (buku_kode) REFERENCES buku(kode_buku)
);

INSERT INTO buku (kode_buku, judul, penulis, tahun, stok) VALUES
  ('BK-001', 'Laskar Pelangi', 'Andrea Hirata', 2005, 3),
  ('BK-002', 'Bumi Manusia', 'Pramoedya Ananta Toer', 1980, 2),
  ('BK-003', 'Hujan Bulan Juni', 'Sapardi Djoko Damono', 1994, 2);
INSERT INTO anggota (nama, kelas, telepon) VALUES
  ('Budi Santoso', 'X-IPA-1', '081234560001'),
  ('Siti Rahmawati', 'X-IPA-2', '081234560002');

-- Pemeriksaan 1: peminjaman mengurangi stok secara atomik.
START TRANSACTION;
INSERT INTO peminjaman (anggota_id, tanggal_pinjam, tanggal_harus_kembali)
  VALUES (1, '2026-01-05', '2026-01-12');
INSERT INTO detail_peminjaman VALUES (1, 'BK-001', 2);
UPDATE buku SET stok = stok - 2 WHERE kode_buku = 'BK-001';
COMMIT;
-- Ekspektasi: BK-001 stok 1
SELECT kode_buku, stok FROM buku WHERE kode_buku = 'BK-001';

-- Pemeriksaan 2: pengembalian mengembalikan stok dan menutup pinjaman.
UPDATE peminjaman SET tanggal_kembali = '2026-01-10' WHERE id = 1;
UPDATE buku SET stok = stok + 2 WHERE kode_buku = 'BK-001';
-- Ekspektasi: BK-001 stok 3
SELECT kode_buku, stok FROM buku WHERE kode_buku = 'BK-001';
-- Ekspektasi: peminjaman 1 sudah memiliki tanggal_kembali
SELECT id, tanggal_kembali FROM peminjaman WHERE id = 1;

-- Pemeriksaan 3: peminjaman kedua (id 2) selesai, peminjaman ketiga (id 3) terbuka.
START TRANSACTION;
INSERT INTO peminjaman (anggota_id, tanggal_pinjam, tanggal_harus_kembali)
  VALUES (2, '2026-01-15', '2026-01-22');
INSERT INTO detail_peminjaman VALUES (2, 'BK-002', 1), (2, 'BK-001', 1);
UPDATE buku SET stok = stok - 1 WHERE kode_buku = 'BK-002';
UPDATE buku SET stok = stok - 1 WHERE kode_buku = 'BK-001';
UPDATE peminjaman SET tanggal_kembali = '2026-01-20' WHERE id = 2;
UPDATE buku SET stok = stok + 1 WHERE kode_buku = 'BK-002';
UPDATE buku SET stok = stok + 1 WHERE kode_buku = 'BK-001';
COMMIT;
START TRANSACTION;
INSERT INTO peminjaman (anggota_id, tanggal_pinjam, tanggal_harus_kembali)
  VALUES (2, '2026-02-01', '2026-02-08');
INSERT INTO detail_peminjaman VALUES (3, 'BK-001', 1);
UPDATE buku SET stok = stok - 1 WHERE kode_buku = 'BK-001';
COMMIT;
-- Ekspektasi: BK-001 total 4, BK-002 total 1, BK-003 total 0
SELECT b.kode_buku,
       COALESCE(SUM(dp.jumlah), 0) AS total_dipinjam
FROM buku b
LEFT JOIN detail_peminjaman dp ON dp.buku_kode = b.kode_buku
GROUP BY b.kode_buku
ORDER BY total_dipinjam DESC, b.kode_buku;

-- Pemeriksaan 4: hanya peminjaman 3 (harus kembali 2026-02-08) yang terlambat.
-- Ekspektasi: 1 baris - peminjaman 3, 20 hari terlambat
SELECT p.id, DATEDIFF('2026-02-28', p.tanggal_harus_kembali) AS hari_terlambat
FROM peminjaman p
WHERE p.tanggal_kembali IS NULL
  AND p.tanggal_harus_kembali < '2026-02-28'
ORDER BY p.id;

-- Pemeriksaan 5: stok akhir konsisten dengan seluruh mutasi.
-- Ekspektasi: BK-001 stok 2, BK-002 stok 2, BK-003 stok 2
SELECT kode_buku, stok FROM buku ORDER BY kode_buku;

DROP DATABASE perpustakaan_db;