Buku panen adalah jantung data koperasi: setiap penimbangan hasil yang tidak tercatat adalah kilogram yang hilang dari total musim, dan setiap salah ketik yang lolos adalah produktivitas yang terdistorsi. Pada bagian ini kita menghidupkan skema Bagian 1 dengan lima petak dan enam belas peristiwa panen sepanjang lima belas bulan, membuktikan bahwa pencatatan yang salah ditolak di depan pintu, lalu mengajukan pertanyaan pertama yang hanya bisa dijawab angka: komoditas mana yang paling banyak dihasilkan, dan petak mana yang paling produktif setelah luasnya ditimbang. Jawaban kedua butuh pembagian hasil oleh luas — kilogram per hektar — dan itulah metrik yang membuat petak setengah hektar bisa mengalahkan petak dua hektar.
Menyiapkan Tabel dan Data Master #
Setiap bagian proyek ini mandiri: berkas pembuka membangun ulang tabel yang dibutuhkan, versi ringkas tanpa COMMENT ON. Bagian ini hanya membutuhkan lahan, komoditas, dan panen — deret harga menunggu Bagian 3. Berikut siapkan.sql:
-- siapkan.sql — fondasi Bagian 2: skema panen versi ringkas + data master.
-- Definisi identik dengan Bagian 1 (tanpa COMMENT); setiap bagian membangun
-- ulang dari nol. harga_pasar tidak dipakai di bagian ini.
CREATE TYPE kualitas_panen AS ENUM ('A', 'B', 'C');
CREATE TABLE lahan (
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nama text NOT NULL UNIQUE,
desa text NOT NULL,
luas_hektar numeric(6,2) NOT NULL CHECK (luas_hektar > 0)
);
CREATE TABLE komoditas (
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nama text NOT NULL UNIQUE
);
CREATE TABLE panen (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
lahan_id integer NOT NULL REFERENCES lahan (id),
komoditas_id integer NOT NULL REFERENCES komoditas (id),
tanggal date NOT NULL,
hasil_kg numeric(10,2) NOT NULL,
kualitas kualitas_panen NOT NULL,
CONSTRAINT hasil_panen_positif CHECK (hasil_kg > 0),
CONSTRAINT satu_panen_per_hari UNIQUE (lahan_id, komoditas_id, tanggal)
);
INSERT INTO lahan (nama, desa, luas_hektar) VALUES
('Sawah Blumbang', 'Blumbang', 1.20),
('Sawah Kaligondang', 'Kaligondang', 0.80),
('Ladang Menoreh', 'Menoreh', 1.50),
('Ladang Sumbing', 'Sumbing', 2.00),
('Kebun Pakem', 'Pakem', 0.50);
INSERT INTO komoditas (nama) VALUES
('padi'),
('jagung'),
('kedelai'),
('kacang tanah');Output yang Dihasilkan:
CREATE TYPE
CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 5
INSERT 0 4Lima petak totalnya 6 hektar: dua sawah padi (1,20 dan 0,80 ha), dua ladang (1,50 dan 2,00 ha) yang menanam jagung dan kedelai bergantian, dan satu kebun kacang tanah 0,50 ha. Luas yang beragam ini disengaja — nanti kita akan melihat bagaimana peringkat petak berubah total antara “paling banyak hasil” dan “paling produktif per hektar”.
Mencatat Enam Belas Panen #
Berkas berikut memuat buku panen lengkap Januari 2025 sampai Maret 2026. Setiap baris mengikuti pola INSERT multi-baris dari chapter INSERT ; kolom id merujuk urutan pemuatan master di atas:
-- catat-panen.sql — buku panen Koperasi Tani Sejahtera, Januari 2025 sampai
-- Maret 2026. Enam belas peristiwa panen; kolom id lahan/komoditas mengikuti
-- urutan pemuatan di siapkan.sql. Padi dua musim per tahun (panen raya Maret,
-- panen kedua Oktober); jagung Februari dan Oktober; kedelai Juni dan
-- Februari; kacang tanah Mei, September, dan Januari.
INSERT INTO panen (lahan_id, komoditas_id, tanggal, hasil_kg, kualitas) VALUES
-- Sawah Blumbang (1), padi (1): 1,2 hektar.
(1, 1, DATE '2025-03-10', 6000.00, 'A'),
(1, 1, DATE '2025-10-18', 5760.00, 'B'),
(1, 1, DATE '2026-03-05', 6240.00, 'A'),
-- Sawah Kaligondang (2), padi (1): 0,8 hektar.
(2, 1, DATE '2025-03-18', 3680.00, 'A'),
(2, 1, DATE '2025-10-25', 3840.00, 'A'),
(2, 1, DATE '2026-03-12', 4000.00, 'A'),
-- Ladang Menoreh (3): jagung (2) Februari/Oktober, kedelai (3) Juni/Februari.
(3, 2, DATE '2025-02-20', 9000.00, 'A'),
(3, 3, DATE '2025-06-18', 2700.00, 'B'),
(3, 2, DATE '2025-10-15', 8250.00, 'A'),
(3, 3, DATE '2026-02-12', 3000.00, 'A'),
-- Ladang Sumbing (4), jagung (2): 2 hektar.
(4, 2, DATE '2025-02-25', 12000.00, 'A'),
(4, 2, DATE '2025-10-20', 11000.00, 'B'),
(4, 2, DATE '2026-02-18', 12600.00, 'A'),
-- Kebun Pakem (5), kacang tanah (4): 0,5 hektar.
(5, 4, DATE '2025-05-10', 850.00, 'B'),
(5, 4, DATE '2025-09-05', 800.00, 'B'),
(5, 4, DATE '2026-01-15', 950.00, 'A');Output yang Dihasilkan:
INSERT 0 16Kalendernya meniru pola tanam nyata. Padi dipanen dua kali setahun: panen raya Maret (musim hujan, hasil terbaik grade A) dan panen kedua Oktober (musim kemarau, hasil sedikit lebih rendah). Jagung jatuh di Februari dan Oktober; kedelai mengisi jeda Juni dan Februari di Ladang Menoreh yang berotasi tanaman; kacang tanah dipanen tiga kali mengikuti umur tanamannya yang pendek. Hasil per panen juga realistis: padi 4,6-5,2 ton per hektar per musim, jagung 5,5-6,3 ton per hektar — angka yang nanti terverifikasi lewat query produktivitas.
Salah Catat Ditolak #
Pencatatan nyata selalu punya salah ketik. Timbangan gagal membaca dan petugas mencatat nol:
-- salah-catat.sql — hasil panen nol harus ditolak CHECK.
-- Berkas ini sengaja berakhir dengan error; psql keluar dengan kode 3.
INSERT INTO panen (lahan_id, komoditas_id, tanggal, hasil_kg, kualitas)
VALUES (5, 4, DATE '2026-01-16', 0.00, 'A');Output yang Dihasilkan:
psql:salah-catat.sql:4: ERROR: new row for relation "panen" violates check constraint "hasil_panen_positif"
DETAIL: Failing row contains (17, 5, 4, 2026-01-16, 0.00, A).Constraint hasil_panen_positif menolak baris itu sebelum menyentuh disk, dan psql keluar kode 3. Inilah makna “pencatatan tervalidasi”: koreksi terjadi di tempat kejadian — petugas menimbang ulang dan mengulang INSERT dengan angka benar, misalnya 950.00 — bukan keesokan harinya setelah laporan bulanan jadi. Tanggal 16 Januari sengaja dipilih berbeda dari panen 15 Januari agar yang teruji murni CHECK hasil nol, bukan UNIQUE tanggal yang sama.
Hasil per Komoditas #
Pertanyaan paling sederhana koperasi: berapa total hasil tiap komoditas seluruh periode? Satu agregasi GROUP BY menjawabnya:
-- hasil-per-komoditas.sql — total hasil seluruh periode per komoditas.
-- count/sum/avg/min/max dalam satu agregasi; total_kg menentukan urutan.
SELECT k.nama AS komoditas,
count(*) AS jumlah_panen,
sum(p.hasil_kg) AS total_kg,
round(avg(p.hasil_kg), 1) AS rata_rata_panen_kg,
min(p.hasil_kg) AS panen_terkecil_kg,
max(p.hasil_kg) AS panen_terbesar_kg
FROM panen p
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY k.nama
ORDER BY total_kg DESC, k.nama;Output yang Dihasilkan:
komoditas | jumlah_panen | total_kg | rata_rata_panen_kg | panen_terkecil_kg | panen_terbesar_kg
--------------+--------------+----------+--------------------+-------------------+-------------------
jagung | 5 | 52850.00 | 10570.0 | 8250.00 | 12600.00
padi | 6 | 29520.00 | 4920.0 | 3680.00 | 6240.00
kedelai | 2 | 5700.00 | 2850.0 | 2700.00 | 3000.00
kacang tanah | 3 | 2600.00 | 866.7 | 800.00 | 950.00
(4 rows)Jagung unggul jauh: 52.850 kg dari lima panen — dua ladang menanamnya dua musim setahun. Padi menyusul 29.520 kg dari enam panen di dua sawah. Perhatikan kolom rata_rata_panen_kg: panen rata-rata jagung (10.570 kg) dua kali lipat padi (4.920 kg), tapi itu cerita lahan terluas, bukan tanaman yang lebih produktif — Ladang Sumbing sendirian menyumbang 2 hektar. Membandingkan komoditas dengan sum() saja akan menyesatkan; perbandingan yang adil menunggu metrik per hektar.
Produktivitas: Kilogram per Hektar #
Inilah query yang membedakan analitik dari pencatatan: hasil dibagi luas petak. Dua query berikut — rincian per petak-komoditas, lalu rekap per petak dengan peringkat:
-- produktivitas-lahan.sql — hasil dibanding luas: kilogram per hektar.
-- Query pertama: rata-rata hasil per hektar per musim, per petak-komoditas.
-- Query kedua: rekap per petak dengan peringkat RANK atas produktivitas
-- gabungan seluruh periode.
SELECT l.nama AS lahan,
k.nama AS komoditas,
count(*) AS jumlah_panen,
round(avg(p.hasil_kg / l.luas_hektar), 0) AS rata_kg_per_hektar
FROM panen p
JOIN lahan l ON l.id = p.lahan_id
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY l.nama, k.nama
ORDER BY rata_kg_per_hektar DESC, lahan, komoditas;
WITH per_lahan AS (
SELECT l.nama AS lahan,
l.luas_hektar,
count(p.id) AS jumlah_panen,
sum(p.hasil_kg) AS total_kg
FROM lahan l
LEFT JOIN panen p ON p.lahan_id = l.id
GROUP BY l.nama, l.luas_hektar
)
SELECT lahan,
luas_hektar,
jumlah_panen,
total_kg,
round(total_kg / luas_hektar, 0) AS kg_per_hektar,
rank() OVER (ORDER BY total_kg / luas_hektar DESC) AS peringkat
FROM per_lahan
ORDER BY peringkat, lahan;Output yang Dihasilkan:
lahan | komoditas | jumlah_panen | rata_kg_per_hektar
-------------------+--------------+--------------+--------------------
Ladang Sumbing | jagung | 3 | 5933
Ladang Menoreh | jagung | 2 | 5750
Sawah Blumbang | padi | 3 | 5000
Sawah Kaligondang | padi | 3 | 4800
Ladang Menoreh | kedelai | 2 | 1900
Kebun Pakem | kacang tanah | 3 | 1733
(6 rows)
lahan | luas_hektar | jumlah_panen | total_kg | kg_per_hektar | peringkat
-------------------+-------------+--------------+----------+---------------+-----------
Ladang Sumbing | 2.00 | 3 | 35600.00 | 17800 | 1
Ladang Menoreh | 1.50 | 4 | 22950.00 | 15300 | 2
Sawah Blumbang | 1.20 | 3 | 18000.00 | 15000 | 3
Sawah Kaligondang | 0.80 | 3 | 11520.00 | 14400 | 4
Kebun Pakem | 0.50 | 3 | 2600.00 | 5200 | 5
(5 rows)Bedah dua keputusan pentingnya:
avg(p.hasil_kg / l.luas_hektar)di query pertama: pembagian terjadi per baris panen sebelum dirata-rata, sehingga hasilnya adalah produktivitas rata-rata per musim tanam — metrik yang lazim dibandingkan antar daerah. Angkanya langsung terbaca sebagai kenyataan agronomi: jagung Ladang Sumbing 5.933 kg/hektar/musim, padi sawah Blumbang 5.000, kedelai Ladang Menoreh 1.900 — kedelai memang tanaman yang hasilnya rendah tapi bernilai tinggi per kilogramnya, tanda yang akan berbalik di Bagian 4.LEFT JOIN+rank()di query kedua:LEFT JOINmenjaga petak yang belum pernah dipanen tetap tampil (dengan total NULL) — kebiasaan yang menyelamatkan laporan ketika petak baru ditambahkan.RANK()lalu memberi peringkat atastotal_kg / luas_hektar, pola RANK dan DENSE RANK pada hasil agregasi.
Hasil panen kini tercatat dan polanya mulai terbaca — tapi bagian cerita terpentingnya justru kapan. Kapan bulan sibuk dan kapan bulan sepi menentukan kebutuhan tenaga kerja, penyimpanan, dan arus kas koperasi.
Kalender Panen #
Dua query berikut memotret pola waktu panen. Query pertama merekap per bulan dengan date_trunc('month', ...) dan merangkum rinciannya lewat string_agg; query kedua membentuk matriks kuartal × komoditas dengan agregasi FILTER — satu baris per kuartal, satu kolom per komoditas:
-- kalender-panen.sql — pola waktu panen dengan date_trunc.
-- Query pertama: rekap bulanan dengan string_agg sebagai rincian ringkas.
-- Query kedua: matriks kuartal x komoditas memakai agregasi FILTER.
SELECT date_trunc('month', p.tanggal)::date AS bulan,
count(*) AS jumlah_panen,
sum(p.hasil_kg) AS total_kg,
string_agg(k.nama || ' ' || p.hasil_kg::int::text || ' kg', ', '
ORDER BY k.nama, p.tanggal) AS rincian
FROM panen p
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY date_trunc('month', p.tanggal)
ORDER BY bulan;
SELECT date_trunc('quarter', p.tanggal)::date AS kuartal,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'padi') AS padi_kg,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'jagung') AS jagung_kg,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'kedelai') AS kedelai_kg,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'kacang tanah') AS kacang_tanah_kg,
sum(p.hasil_kg) AS total_kg
FROM panen p
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY date_trunc('quarter', p.tanggal)
ORDER BY kuartal;Output yang Dihasilkan:
bulan | jumlah_panen | total_kg | rincian
------------+--------------+----------+-------------------------------------------------------------
2025-02-01 | 2 | 21000.00 | jagung 9000 kg, jagung 12000 kg
2025-03-01 | 2 | 9680.00 | padi 6000 kg, padi 3680 kg
2025-05-01 | 1 | 850.00 | kacang tanah 850 kg
2025-06-01 | 1 | 2700.00 | kedelai 2700 kg
2025-09-01 | 1 | 800.00 | kacang tanah 800 kg
2025-10-01 | 4 | 28850.00 | jagung 8250 kg, jagung 11000 kg, padi 5760 kg, padi 3840 kg
2026-01-01 | 1 | 950.00 | kacang tanah 950 kg
2026-02-01 | 2 | 15600.00 | jagung 12600 kg, kedelai 3000 kg
2026-03-01 | 2 | 10240.00 | padi 6240 kg, padi 4000 kg
(9 rows)
kuartal | padi_kg | jagung_kg | kedelai_kg | kacang_tanah_kg | total_kg
------------+----------+-----------+------------+-----------------+----------
2025-01-01 | 9680.00 | 21000.00 | | | 30680.00
2025-04-01 | | | 2700.00 | 850.00 | 3550.00
2025-07-01 | | | | 800.00 | 800.00
2025-10-01 | 9600.00 | 19250.00 | | | 28850.00
2026-01-01 | 10240.00 | 12600.00 | 3000.00 | 950.00 | 26790.00
(5 rows)Kalendernya bersisik dan mudah dibaca: Februari-Maret adalah puncak semester pertama (jagung ladang lalu panen raya padi), Juni-September nyaris sepi kecuali kedelai dan kacang tanah, lalu Oktober meledak — empat panen sekaligus sebanyak 28.850 kg dalam satu bulan, gabungan panen kedua padi dan jagung. Matriks kuartal memperlihatkan pola yang sama dalam lima baris: kuartal I dan IV adalah musim ramai, kuartal II-III jeda. date_trunc memotong tanggal ke awal periode seperti yang dibahas di chapter Date dan Time
, dan pola FILTER per komoditas persis varian FILTER pada Aggregate
yang menghindari lima JOIN tambahan.
Buku panen kini utuh: hasil, produktivitas, dan kalender. Namun nilai koperasi ditentukan bukan hanya oleh kilogram, melainkan oleh harga ketika kilogram itu dijual. Bagian Pencatatan Harga Komoditas membangun sisi kedua itu: deret harga harian yang berdetak setiap hari, panen atau tidak.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
catat-panen.sql
Unduh berkas-- catat-panen.sql — buku panen Koperasi Tani Sejahtera, Januari 2025 sampai
-- Maret 2026. Enam belas peristiwa panen; kolom id lahan/komoditas mengikuti
-- urutan pemuatan di siapkan.sql. Padi dua musim per tahun (panen raya Maret,
-- panen kedua Oktober); jagung Februari dan Oktober; kedelai Juni dan
-- Februari; kacang tanah Mei, September, dan Januari.
INSERT INTO panen (lahan_id, komoditas_id, tanggal, hasil_kg, kualitas) VALUES
-- Sawah Blumbang (1), padi (1): 1,2 hektar.
(1, 1, DATE '2025-03-10', 6000.00, 'A'),
(1, 1, DATE '2025-10-18', 5760.00, 'B'),
(1, 1, DATE '2026-03-05', 6240.00, 'A'),
-- Sawah Kaligondang (2), padi (1): 0,8 hektar.
(2, 1, DATE '2025-03-18', 3680.00, 'A'),
(2, 1, DATE '2025-10-25', 3840.00, 'A'),
(2, 1, DATE '2026-03-12', 4000.00, 'A'),
-- Ladang Menoreh (3): jagung (2) Februari/Oktober, kedelai (3) Juni/Februari.
(3, 2, DATE '2025-02-20', 9000.00, 'A'),
(3, 3, DATE '2025-06-18', 2700.00, 'B'),
(3, 2, DATE '2025-10-15', 8250.00, 'A'),
(3, 3, DATE '2026-02-12', 3000.00, 'A'),
-- Ladang Sumbing (4), jagung (2): 2 hektar.
(4, 2, DATE '2025-02-25', 12000.00, 'A'),
(4, 2, DATE '2025-10-20', 11000.00, 'B'),
(4, 2, DATE '2026-02-18', 12600.00, 'A'),
-- Kebun Pakem (5), kacang tanah (4): 0,5 hektar.
(5, 4, DATE '2025-05-10', 850.00, 'B'),
(5, 4, DATE '2025-09-05', 800.00, 'B'),
(5, 4, DATE '2026-01-15', 950.00, 'A');
hasil-per-komoditas.sql
Unduh berkas-- hasil-per-komoditas.sql — total hasil seluruh periode per komoditas.
-- count/sum/avg/min/max dalam satu agregasi; total_kg menentukan urutan.
SELECT k.nama AS komoditas,
count(*) AS jumlah_panen,
sum(p.hasil_kg) AS total_kg,
round(avg(p.hasil_kg), 1) AS rata_rata_panen_kg,
min(p.hasil_kg) AS panen_terkecil_kg,
max(p.hasil_kg) AS panen_terbesar_kg
FROM panen p
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY k.nama
ORDER BY total_kg DESC, k.nama;
kalender-panen.sql
Unduh berkas-- kalender-panen.sql — pola waktu panen dengan date_trunc.
-- Query pertama: rekap bulanan dengan string_agg sebagai rincian ringkas.
-- Query kedua: matriks kuartal x komoditas memakai agregasi FILTER.
SELECT date_trunc('month', p.tanggal)::date AS bulan,
count(*) AS jumlah_panen,
sum(p.hasil_kg) AS total_kg,
string_agg(k.nama || ' ' || p.hasil_kg::int::text || ' kg', ', '
ORDER BY k.nama, p.tanggal) AS rincian
FROM panen p
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY date_trunc('month', p.tanggal)
ORDER BY bulan;
SELECT date_trunc('quarter', p.tanggal)::date AS kuartal,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'padi') AS padi_kg,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'jagung') AS jagung_kg,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'kedelai') AS kedelai_kg,
sum(p.hasil_kg) FILTER (WHERE k.nama = 'kacang tanah') AS kacang_tanah_kg,
sum(p.hasil_kg) AS total_kg
FROM panen p
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY date_trunc('quarter', p.tanggal)
ORDER BY kuartal;
produktivitas-lahan.sql
Unduh berkas-- produktivitas-lahan.sql — hasil dibanding luas: kilogram per hektar.
-- Query pertama: rata-rata hasil per hektar per musim, per petak-komoditas.
-- Query kedua: rekap per petak dengan peringkat RANK atas produktivitas
-- gabungan seluruh periode.
SELECT l.nama AS lahan,
k.nama AS komoditas,
count(*) AS jumlah_panen,
round(avg(p.hasil_kg / l.luas_hektar), 0) AS rata_kg_per_hektar
FROM panen p
JOIN lahan l ON l.id = p.lahan_id
JOIN komoditas k ON k.id = p.komoditas_id
GROUP BY l.nama, k.nama
ORDER BY rata_kg_per_hektar DESC, lahan, komoditas;
WITH per_lahan AS (
SELECT l.nama AS lahan,
l.luas_hektar,
count(p.id) AS jumlah_panen,
sum(p.hasil_kg) AS total_kg
FROM lahan l
LEFT JOIN panen p ON p.lahan_id = l.id
GROUP BY l.nama, l.luas_hektar
)
SELECT lahan,
luas_hektar,
jumlah_panen,
total_kg,
round(total_kg / luas_hektar, 0) AS kg_per_hektar,
rank() OVER (ORDER BY total_kg / luas_hektar DESC) AS peringkat
FROM per_lahan
ORDER BY peringkat, lahan;
salah-catat.sql
Unduh berkas-- salah-catat.sql — hasil panen nol harus ditolak CHECK.
-- Berkas ini sengaja berakhir dengan error; psql keluar dengan kode 3.
INSERT INTO panen (lahan_id, komoditas_id, tanggal, hasil_kg, kualitas)
VALUES (5, 4, DATE '2026-01-16', 0.00, 'A');
siapkan.sql
Unduh berkas-- siapkan.sql — fondasi Bagian 2: skema panen versi ringkas + data master.
-- Definisi identik dengan Bagian 1 (tanpa COMMENT); setiap bagian membangun
-- ulang dari nol. harga_pasar tidak dipakai di bagian ini.
CREATE TYPE kualitas_panen AS ENUM ('A', 'B', 'C');
CREATE TABLE lahan (
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nama text NOT NULL UNIQUE,
desa text NOT NULL,
luas_hektar numeric(6,2) NOT NULL CHECK (luas_hektar > 0)
);
CREATE TABLE komoditas (
id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
nama text NOT NULL UNIQUE
);
CREATE TABLE panen (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
lahan_id integer NOT NULL REFERENCES lahan (id),
komoditas_id integer NOT NULL REFERENCES komoditas (id),
tanggal date NOT NULL,
hasil_kg numeric(10,2) NOT NULL,
kualitas kualitas_panen NOT NULL,
CONSTRAINT hasil_panen_positif CHECK (hasil_kg > 0),
CONSTRAINT satu_panen_per_hari UNIQUE (lahan_id, komoditas_id, tanggal)
);
INSERT INTO lahan (nama, desa, luas_hektar) VALUES
('Sawah Blumbang', 'Blumbang', 1.20),
('Sawah Kaligondang', 'Kaligondang', 0.80),
('Ladang Menoreh', 'Menoreh', 1.50),
('Ladang Sumbing', 'Sumbing', 2.00),
('Kebun Pakem', 'Pakem', 0.50);
INSERT INTO komoditas (nama) VALUES
('padi'),
('jagung'),
('kedelai'),
('kacang tanah');