Rata-rata adalah angka yang paling sering diminta dan paling sering disalahartikan. “Berapa rata-rata belanja pelanggan kita?” terdengar sederhana, tetapi jawabannya bergantung pada dua keputusan tersembunyi: rata-rata dari nilai apa, dan dibagi berapa. Fungsi avg menghitung total nilai lalu membaginya dengan banyaknya nilai — dan di situlah letak kejutannya: avg pada kolom integer menghasilkan numeric penuh desimal, NULL diabaikan dari pembagi, dan rata-rata sederhana sering kali menjawab pertanyaan yang salah ketika setiap nilai punya bobot berbeda. Chapter ini membedah satu per satu memakai tabel penjualan yang sudah menemani kita sejak chapter COUNT
.
Tabel Latihan: penjualan #
Tabel yang sama untuk seluruh section: 12 transaksi Toko Maju Jaya, Januari–Februari 2026. Dua kolom sengaja memuat NULL — diskon kosong pada 6 baris, kategori kosong pada 2 baris — karena pembagi rata-rata adalah tempat NULL paling sering menjebak:
-- Tabel latihan utama Section Agregasi Data:
-- transaksi penjualan Toko Maju Jaya, Januari-Februari 2026.
CREATE TABLE penjualan (
id integer PRIMARY KEY,
tanggal date NOT NULL,
penjual text NOT NULL,
kategori text, -- NULL = transaksi belum dikategorikan
produk text NOT NULL,
jumlah integer NOT NULL, -- unit terjual
harga numeric(10,2) NOT NULL, -- harga satuan, rupiah
diskon numeric(5,2) -- NULL = tanpa diskon
);
INSERT INTO penjualan (id, tanggal, penjual, kategori, produk, jumlah, harga, diskon) VALUES
( 1, DATE '2026-01-05', 'Budi', 'Kopi', 'Kopi Gayo', 10, 85000.00, 5.00),
( 2, DATE '2026-01-08', 'Siti', 'Teh', 'Teh Melati', 25, 35000.00, NULL),
( 3, DATE '2026-01-12', 'Andi', 'Gula', 'Gula Merah', 40, 15000.00, NULL),
( 4, DATE '2026-01-15', 'Budi', 'Kopi', 'Kopi Toraja', 8, 90000.00, 10.00),
( 5, DATE '2026-01-20', 'Dewi', NULL, 'Kopi Gayo', 12, 85000.00, NULL),
( 6, DATE '2026-01-22', 'Siti', 'Teh', 'Teh Hijau', 15, 40000.00, 7.50),
( 7, DATE '2026-01-25', 'Andi', 'Gula', 'Gula Merah', 30, 15000.00, 2.50),
( 8, DATE '2026-02-02', 'Budi', NULL, 'Teh Melati', 20, 35000.00, NULL),
( 9, DATE '2026-02-05', 'Dewi', 'Kopi', 'Kopi Gayo', 18, 85000.00, 8.00),
(10, DATE '2026-02-10', 'Siti', 'Teh', 'Teh Melati', 22, 35000.00, NULL),
(11, DATE '2026-02-14', 'Andi', 'Gula', 'Gula Merah', 35, 15000.00, NULL),
(12, DATE '2026-02-18', 'Budi', 'Kopi', 'Kopi Toraja', 6, 90000.00, 12.00);
SELECT id, tanggal, penjual, kategori, produk, jumlah, harga, diskon
FROM penjualan
ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 12
id | tanggal | penjual | kategori | produk | jumlah | harga | diskon
----+------------+---------+----------+-------------+--------+----------+--------
1 | 2026-01-05 | Budi | Kopi | Kopi Gayo | 10 | 85000.00 | 5.00
2 | 2026-01-08 | Siti | Teh | Teh Melati | 25 | 35000.00 |
3 | 2026-01-12 | Andi | Gula | Gula Merah | 40 | 15000.00 |
4 | 2026-01-15 | Budi | Kopi | Kopi Toraja | 8 | 90000.00 | 10.00
5 | 2026-01-20 | Dewi | | Kopi Gayo | 12 | 85000.00 |
6 | 2026-01-22 | Siti | Teh | Teh Hijau | 15 | 40000.00 | 7.50
7 | 2026-01-25 | Andi | Gula | Gula Merah | 30 | 15000.00 | 2.50
8 | 2026-02-02 | Budi | | Teh Melati | 20 | 35000.00 |
9 | 2026-02-05 | Dewi | Kopi | Kopi Gayo | 18 | 85000.00 | 8.00
10 | 2026-02-10 | Siti | Teh | Teh Melati | 22 | 35000.00 |
11 | 2026-02-14 | Andi | Gula | Gula Merah | 35 | 15000.00 |
12 | 2026-02-18 | Budi | Kopi | Kopi Toraja | 6 | 90000.00 | 12.00
(12 rows)Rata-rata pada Kolom Integer #
Kita mulai dari kolom jumlah yang bertipe integer. Total 241 unit tersebar di 12 transaksi, jadi rata-ratanya 241 ÷ 12 — angka yang tidak bulat, dan inilah kebiasaan pertama avg:
-- AVG = total nilai dibagi banyaknya nilai: 241 unit dibagi 12 transaksi.
SELECT avg(jumlah) AS rata_mentah
FROM penjualan;
-- Tipe hasilnya numeric — bukan integer — meski kolomnya integer.
SELECT pg_typeof(avg(jumlah)) AS tipe_hasil
FROM penjualan;
-- Dua cara merapikan hasilnya:
-- round() membulatkan nilainya (tetap numeric),
-- to_char() hanya memformat tampilan (hasilnya text).
SELECT round(avg(jumlah), 2) AS rata_dibulatkan,
to_char(avg(jumlah), 'FM9999990.00') AS rata_tampilan
FROM penjualan;Output yang Dihasilkan:
rata_mentah
---------------------
20.0833333333333333
(1 row)
tipe_hasil
------------
numeric
(1 row)
rata_dibulatkan | rata_tampilan
-----------------+---------------
20.08 | 20.08
(1 row)avg(jumlah) menghasilkan 20.0833333333333333 — enam belas digit yang benar secara matematis. Ini bukan kebetulan desimal: pg_typeof membuktikan tipe hasilnya numeric, padahal kolomnya integer. PostgreSQL sengaja menghindari pembagian bilangan bulat yang akan memotong hasil menjadi 20; avg selalu mengembalikan tipe pecahan supaya presisi tidak hilang diam-diam. Untuk tampilan laporan, ada dua alat yang sering tertukar:
round(avg(jumlah), 2)membulatkan nilainya menjadi20.08— hasilnya tetap bertipenumericdan masih bisa dihitung lanjutan.to_char(avg(jumlah), 'FM9999990.00')hanya memformat tampilan menjadi teks20.08— nilai aslinya tidak tersentuh, dan hasilnya sudah menjaditextyang tidak bisa langsung dijumlahkan lagi.
Pilih round saat angkanya masih dipakai hitungan berikutnya; pilih to_char (atau pemformatan di sisi aplikasi) saat angkanya memang menuju tampilan akhir.
AVG Mengabaikan NULL — Termasuk dari Pembagi #
Kejutan kedua avg menyatu dengan sum: NULL tidak dihitung sebagai nol, dan lebih penting lagi, NULL juga tidak ikut sebagai pembagi:
-- AVG mengabaikan NULL: hanya baris dengan diskon terisi yang dihitung.
SELECT avg(diskon) AS rata_diskon,
count(diskon) AS baris_dihitung
FROM penjualan;
-- Bukti persamaannya: avg(x) identik dengan sum(x) / count(x).
SELECT sum(diskon) / count(diskon) AS lewat_sum_dibagi_count
FROM penjualan;
-- Ganti pembaginya menjadi count(*) dan maknanya berubah total:
-- 45.00 dibagi 12 transaksi = rata-rata diskon jika NULL dianggap "tanpa diskon".
SELECT sum(diskon) / count(*) AS rata_terhadap_semua_transaksi
FROM penjualan;Output yang Dihasilkan:
rata_diskon | baris_dihitung
--------------------+----------------
7.5000000000000000 | 6
(1 row)
lewat_sum_dibagi_count
------------------------
7.5000000000000000
(1 row)
rata_terhadap_semua_transaksi
-------------------------------
3.7500000000000000
(1 row)Query pertama dan kedua menghasilkan angka yang identik: 7.5. Total diskon 45.00 dibagi 6 baris terisi — bukan dibagi 12. Inilah persamaan resminya: avg(x) setara dengan sum(x) / count(x), dan karena count(x) melewatkan NULL, pembagi otomatis menyesuaikan. Query ketiga sengaja memakai pembagi yang berbeda — count(*) — dan hasilnya anjlok menjadi 3.75: total diskon yang sama dibagi rata ke seluruh 12 transaksi. Keduanya benar; keduanya menjawab pertanyaan berbeda. “Rata-rata diskon transaksi yang berdiskon” (7.5) lawan “rerata diskon efektif di seluruh transaksi” (3.75) adalah keputusan bisnis, dan avg memihak yang pertama tanpa bertanya.
Rata-rata Tertimbang #
Kejutan ketiga muncul saat setiap nilai punya bobot. Rata-rata sederhana avg(harga) memperlakukan harga 15000 yang terjual 40 unit sama beratnya dengan harga 90000 yang terjual 6 unit — seolah setiap harga muncul satu kali:
-- Rata-rata sederhana: setiap harga satuan dianggap setara bobotnya.
SELECT avg(harga) AS rata_harga_satuan
FROM penjualan;
-- Rata-rata tertimbang: setiap harga "ditimbang" dengan unit yang terjual —
-- total rupiah dibagi total unit. Produk murah yang terjual banyak menarik
-- hasil ke bawah.
SELECT sum(jumlah * harga) / sum(jumlah) AS rata_biaya_per_item
FROM penjualan;
-- Keduanya berdampingan: selisih 52083.33 lawan 38091.29 adalah bukti
-- bahwa "rata-rata apa" harus diputuskan sadar, bukan kebetulan.
SELECT round(avg(harga), 2) AS rata_sederhana,
round(sum(jumlah * harga) / sum(jumlah), 2) AS rata_tertimbang,
sum(jumlah) AS total_unit
FROM penjualan;Output yang Dihasilkan:
rata_harga_satuan
--------------------
52083.333333333333
(1 row)
rata_biaya_per_item
---------------------
38091.286307053942
(1 row)
rata_sederhana | rata_tertimbang | total_unit
----------------+-----------------+------------
52083.33 | 38091.29 | 241
(1 row)Mari kita bedah pola rata-rata tertimbangnya:
sum(jumlah * harga)menjumlahkan nilai rupiah seluruh transaksi — pembilangnya:9180000.00.sum(jumlah)menjumlahkan seluruh unit terjual — pembaginya:241.- Pembagian keduanya menghasilkan
38091.286307053942— biaya rata-rata per satu item yang benar-benar terjual.
Hasilnya jauh berbeda dari avg(harga) yang menghasilkan 52083.33. Selisahnya ada karena Gula Merah yang murah (15000) menyumbang 105 unit — menarik biaya per item ke bawah — sedangkan rata-rata sederhana sama sekali tidak melihat unit. Pola sum(a × b) / sum(b) ini berlaku umum untuk semua rata-rata berbobot: nilai rapor tertimbang SKS, harga pokok rata-rata tertimbang volume pembelian, sampai durasi rata-rata tertimbang jumlah request.
Total dan rata-rata kini dikuasai; dua pertanyaan laporan tersisa berada di kutub berlawanan: nilai terkecil dan terbesar. Chapter berikutnya membahas min dan max — dari angka, teks, sampai tanggal: MIN dan MAX
.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
avg-dasar.sql
Unduh berkas-- AVG = total nilai dibagi banyaknya nilai: 241 unit dibagi 12 transaksi.
SELECT avg(jumlah) AS rata_mentah
FROM penjualan;
-- Tipe hasilnya numeric — bukan integer — meski kolomnya integer.
SELECT pg_typeof(avg(jumlah)) AS tipe_hasil
FROM penjualan;
-- Dua cara merapikan hasilnya:
-- round() membulatkan nilainya (tetap numeric),
-- to_char() hanya memformat tampilan (hasilnya text).
SELECT round(avg(jumlah), 2) AS rata_dibulatkan,
to_char(avg(jumlah), 'FM9999990.00') AS rata_tampilan
FROM penjualan;
avg-null.sql
Unduh berkas-- AVG mengabaikan NULL: hanya baris dengan diskon terisi yang dihitung.
SELECT avg(diskon) AS rata_diskon,
count(diskon) AS baris_dihitung
FROM penjualan;
-- Bukti persamaannya: avg(x) identik dengan sum(x) / count(x).
SELECT sum(diskon) / count(diskon) AS lewat_sum_dibagi_count
FROM penjualan;
-- Ganti pembaginya menjadi count(*) dan maknanya berubah total:
-- 45.00 dibagi 12 transaksi = rata-rata diskon jika NULL dianggap "tanpa diskon".
SELECT sum(diskon) / count(*) AS rata_terhadap_semua_transaksi
FROM penjualan;
avg-tertimbang.sql
Unduh berkas-- Rata-rata sederhana: setiap harga satuan dianggap setara bobotnya.
SELECT avg(harga) AS rata_harga_satuan
FROM penjualan;
-- Rata-rata tertimbang: setiap harga "ditimbang" dengan unit yang terjual —
-- total rupiah dibagi total unit. Produk murah yang terjual banyak menarik
-- hasil ke bawah.
SELECT sum(jumlah * harga) / sum(jumlah) AS rata_biaya_per_item
FROM penjualan;
-- Keduanya berdampingan: selisih 52083.33 lawan 38091.29 adalah bukti
-- bahwa "rata-rata apa" harus diputuskan sadar, bukan kebetulan.
SELECT round(avg(harga), 2) AS rata_sederhana,
round(sum(jumlah * harga) / sum(jumlah), 2) AS rata_tertimbang,
sum(jumlah) AS total_unit
FROM penjualan;
buat-tabel.sql
Unduh berkas-- Tabel latihan utama Section Agregasi Data:
-- transaksi penjualan Toko Maju Jaya, Januari-Februari 2026.
CREATE TABLE penjualan (
id integer PRIMARY KEY,
tanggal date NOT NULL,
penjual text NOT NULL,
kategori text, -- NULL = transaksi belum dikategorikan
produk text NOT NULL,
jumlah integer NOT NULL, -- unit terjual
harga numeric(10,2) NOT NULL, -- harga satuan, rupiah
diskon numeric(5,2) -- NULL = tanpa diskon
);
INSERT INTO penjualan (id, tanggal, penjual, kategori, produk, jumlah, harga, diskon) VALUES
( 1, DATE '2026-01-05', 'Budi', 'Kopi', 'Kopi Gayo', 10, 85000.00, 5.00),
( 2, DATE '2026-01-08', 'Siti', 'Teh', 'Teh Melati', 25, 35000.00, NULL),
( 3, DATE '2026-01-12', 'Andi', 'Gula', 'Gula Merah', 40, 15000.00, NULL),
( 4, DATE '2026-01-15', 'Budi', 'Kopi', 'Kopi Toraja', 8, 90000.00, 10.00),
( 5, DATE '2026-01-20', 'Dewi', NULL, 'Kopi Gayo', 12, 85000.00, NULL),
( 6, DATE '2026-01-22', 'Siti', 'Teh', 'Teh Hijau', 15, 40000.00, 7.50),
( 7, DATE '2026-01-25', 'Andi', 'Gula', 'Gula Merah', 30, 15000.00, 2.50),
( 8, DATE '2026-02-02', 'Budi', NULL, 'Teh Melati', 20, 35000.00, NULL),
( 9, DATE '2026-02-05', 'Dewi', 'Kopi', 'Kopi Gayo', 18, 85000.00, 8.00),
(10, DATE '2026-02-10', 'Siti', 'Teh', 'Teh Melati', 22, 35000.00, NULL),
(11, DATE '2026-02-14', 'Andi', 'Gula', 'Gula Merah', 35, 15000.00, NULL),
(12, DATE '2026-02-18', 'Budi', 'Kopi', 'Kopi Toraja', 6, 90000.00, 12.00);
SELECT id, tanggal, penjual, kategori, produk, jumlah, harga, diskon
FROM penjualan
ORDER BY id;