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:

sql 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:

text
CREATE TYPE
CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 5
INSERT 0 4

Lima 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:

sql catat-panen.sql
-- 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:

text
INSERT 0 16

Kalendernya 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:

sql salah-catat.sql
-- 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:

text
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:

text
  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:

text
       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:

  1. 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.
  2. LEFT JOIN + rank() di query kedua: LEFT JOIN menjaga petak yang belum pernah dipanen tetap tampil (dengan total NULL) — kebiasaan yang menyelamatkan laporan ketika petak baru ditambahkan. RANK() lalu memberi peringkat atas total_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:

text
   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
sql
-- 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
sql
-- 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
sql
-- 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
sql
-- 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
sql
-- 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
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');