Harga satuan, jumlah stok, selisih kas — angka adalah penghuni tetap kolom database, dan pengolahannya penuh aturan yang tidak terlihat mata: pembulatan yang arahnya berbeda antara numeric dan float, sisa bagi yang berguna untuk pengelompokan, hingga akar dan pangkat untuk rumus perhitungan. Kesalahan kecil di pemilihan fungsi pembulatan bisa berujung pada laporan keuangan yang tidak cocok satu rupiah pun dengan buku besar. Chapter ini memetakan fungsi matematika bawaan PostgreSQL yang paling sering dipakai sehari-hari, sekaligus membuktikan langsung di server latihan perbedaan perilaku pembulatan yang menjadi sumber kejutan paling sering di keluarga fungsi ini.
Nilai Absolut dan Tanda #
Dua fungsi pembuka yang tampak sederhana tetapi sangat praktis untuk data kas: abs() membuang tanda minus, sedangkan sign() meringkas arah sebuah nilai menjadi -1, 0, atau 1. Untuk mendemonstrasikannya, kita pakai tabel mutasi kas kecil milik Toko Maju Jaya yang mencatat pemasukan sebagai angka positif dan pengeluaran sebagai angka negatif:
-- tabel mutasi kas sederhana untuk demo abs() dan sign()
CREATE TABLE mutasi (
id integer PRIMARY KEY,
keterangan text NOT NULL,
jumlah integer NOT NULL
);
INSERT INTO mutasi (id, keterangan, jumlah) VALUES
(1, 'penjualan tunai', 250000),
(2, 'pengeluaran belanja kopi', -175000),
(3, 'pengeluaran sewa kios', -500000),
(4, 'penjualan kredit', 125000);
SELECT keterangan, jumlah,
abs(jumlah) AS nilai_absolut,
sign(jumlah) AS arah
FROM mutasi
ORDER BY id;
-- sign() mengembalikan -1, 0, atau 1
SELECT sign(-7) AS negatif,
sign(0) AS nol,
sign(7.5) AS positif;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 4
keterangan | jumlah | nilai_absolut | arah
--------------------------+---------+---------------+------
penjualan tunai | 250000 | 250000 | 1
pengeluaran belanja kopi | -175000 | 175000 | -1
pengeluaran sewa kios | -500000 | 500000 | -1
penjualan kredit | 125000 | 125000 | 1
(4 rows)
negatif | nol | positif
---------+-----+---------
-1 | 0 | 1
(1 row)Kolom nilai_absolut menampilkan besaran uang tanpa memandang arah — berguna ketika laporan hanya ingin tahu “berapa besar nilainya”, misalnya untuk menghitung total volume transaksi tanpa membedakan masuk dan keluar. Kolom arah memampatkan informasi yang sama menjadi tiga kemungkinan nilai; sign() menerima angka pecahan apa pun tetapi hasilnya selalu bilangan bulat -1, 0, atau 1, sehingga cocok dipakai sebagai penanda dalam CASE atau filter cepat.
Pembulatan round dan Perbedaan numeric vs float #
Fungsi round() adalah fungsi numerik yang paling sering salah dipahami, karena PostgreSQL memelihara dua perilaku sekaligus tergantung tipe argumennya. Pada numeric, nilai setengah dibulatkan menjauhi nol; pada double precision, pembulatan mengikuti aturan IEEE round half to even — nilai setengah dibulatkan ke bilangan genap terdekat:
| Ekspresi | Hasil | Aturan yang berlaku |
|---|---|---|
round(2.5) | 3 | numeric: setengah menjauhi nol |
round(-2.5) | -3 | numeric: setengah menjauhi nol |
round(2.5::float8) | 2 | float: setengah ke genap terdekat |
round(0.5::float8) | 0 | float: setengah ke genap terdekat |
round(3.5::float8) | 4 | float: setengah ke genap terdekat |
-- round() pada numeric: nilai .5 dibulatkan menjauhi nol
SELECT round(2.5) AS positif_setengah,
round(-2.5) AS negatif_setengah,
round(2.4) AS di_bawah_setengah;
-- round() dengan jumlah angka desimal
SELECT round(123.456, 2) AS dua_desimal,
round(123.456, 0) AS tanpa_desimal;
-- round() dengan tempat negatif: pembulatan puluhan dan ratusan
SELECT round(1234, -2) AS ratusan_bawah,
round(1251, -2) AS ratusan_atas;
-- scale() melaporkan jumlah digit di belakang koma
SELECT scale(12.3450) AS skala_nilai;Output yang Dihasilkan:
positif_setengah | negatif_setengah | di_bawah_setengah
------------------+------------------+-------------------
3 | -3 | 2
(1 row)
dua_desimal | tanpa_desimal
-------------+---------------
123.46 | 123
(1 row)
ratusan_bawah | ratusan_atas
---------------+--------------
1200 | 1300
(1 row)
skala_nilai
-------------
4
(1 row)Tiga perilaku pada contoh di atas layak dicermati:
round(2.5)danround(-2.5): literal2.5tanpa anotasi tipe ditafsirkan sebagainumeric, sehingga aturan “menjauhi nol” berlaku — hasilnya3dan-3.round(1234, -2): tempat negatif membulatkan pada digit ratusan, sehingga1234menjadi1200dan1251melompat ke1300— trik yang sering dipakai untuk mengelompokkan nilai harga ke dalam rentang ratusan ribu atau jutaan.scale(12.3450): mengembalikan4karena nilainumericmenyimpan angka nol di belakang koma sebagai bagian dari skalanya. Presisi dan skala tipenumeric(p,s)sudah dibahas tuntas di chapter Numeric dan Decimal .
Sekarang giliran sisi float dari pembulatan, lengkap dengan perilaku spesial NaN (not a number) yang hanya dimiliki tipe floating-point:
-- pembulatan float mengikuti aturan "half to even" (dibulatkan ke angka genap)
SELECT round(2.5::float8) AS float_dua_setengah,
round(0.5::float8) AS float_nol_koma_lima,
round(3.5::float8) AS float_tiga_koma_lima;
-- NaN pada float dibandingkan seolah-olah lebih besar dari angka apa pun
SELECT 'NaN'::float8 > 100 AS nan_lebih_besar,
'NaN'::float8 = 'NaN'::float8 AS nan_sama_dengan_nan;
-- pembulatan NaN tetap menghasilkan NaN
SELECT round('NaN'::float8) AS bulatkan_nan;Output yang Dihasilkan:
float_dua_setengah | float_nol_koma_lima | float_tiga_koma_lima
--------------------+---------------------+----------------------
2 | 0 | 4
(1 row)
nan_lebih_besar | nan_sama_dengan_nan
-----------------+---------------------
t | t
(1 row)
bulatkan_nan
--------------
NaN
(1 row)Hasil 2, 0, dan 4 untuk ketiga pembulatan float itu bukan kebetulan: 2.5 jatuh tepat di tengah antara 2 dan 3, dan aturan half to even memilih bilangan genap — 2. Nilai 0.5 dibulatkan ke 0 karena 0 genap, sedangkan 3.5 dibulatkan ke 4 karena 4 genap. Perilaku NaN juga khas PostgreSQL: NaN dianggap lebih besar dari angka biasa apa pun, dan NaN = NaN mengembalikan true — berbeda dari standar IEEE yang menyatakan perbandingan dengan NaN selalu false. Pembulatan NaN pun tidak error, hasilnya tetap NaN.
-- round() dua argumen hanya tersedia untuk numeric, bukan double precision
SELECT round(3.14159::float8, 2) AS gagal;Output yang Dihasilkan:
psql:round-float-error.sql:2: ERROR: function round(double precision, integer) does not exist
LINE 1: SELECT round(3.14159::float8, 2) AS gagal;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.Error function round(double precision, integer) does not exist menyatakan persis masalahnya: PostgreSQL mencari fungsi round yang menerima double precision dan integer, dan tidak menemukannya. Solusinya ada dua — ubah argumen menjadi numeric dengan round(3.14159::numeric, 2), atau bulatkan bertahap dengan round(3.14159::float8) lalu atur tampilannya di sisi aplikasi. Pilihan pertama lebih tepat untuk perhitungan uang.
Pembulatan Arah: ceil, floor, dan trunc #
Ketika arah pembulatan tidak boleh diserahkan pada nilai desimalnya, tiga fungsi berikut memberi kendali penuh — ceil() selalu ke atas, floor() selalu ke bawah, dan trunc() memotong tanpa peduli arah:
| Fungsi | Arah | Contoh | Hasil |
|---|---|---|---|
ceil(x) / ceiling(x) | Ke atas (menuju +∞) | ceil(3.1) | 4 |
floor(x) | Ke bawah (menuju -∞) | floor(3.9) | 3 |
trunc(x) | Menuju nol | trunc(-3.9) | -3 |
trunc(x, n) | Menuju nol, sisakan n desimal | trunc(123.456, 1) | 123.4 |
-- ceil()/ceiling() membulatkan ke atas, floor() ke bawah
SELECT ceil(3.1) AS ke_atas,
ceiling(3.1) AS padanan_ceiling,
floor(3.9) AS ke_bawah,
ceil(-3.1) AS negatif_ke_atas,
floor(-3.1) AS negatif_ke_bawah;
-- trunc() memotong angka desimal tanpa membulatkan
SELECT trunc(3.9) AS potong_positif,
trunc(-3.9) AS potong_negatif,
trunc(123.456, 1) AS potong_satu_desimal,
trunc(123.456, -1) AS potong_puluhan;Output yang Dihasilkan:
ke_atas | padanan_ceiling | ke_bawah | negatif_ke_atas | negatif_ke_bawah
---------+-----------------+----------+-----------------+------------------
4 | 4 | 3 | -3 | -4
(1 row)
potong_positif | potong_negatif | potong_satu_desimal | potong_puluhan
----------------+----------------+---------------------+----------------
3 | -3 | 123.4 | 120
(1 row)Perbedaan yang paling penting justru muncul pada bilangan negatif: ceil(-3.1) menghasilkan -3 (ke atas berarti menuju nol), floor(-3.1) menghasilkan -4 (ke bawah berarti menjauhi nol), sedangkan trunc(-3.9) menghasilkan -3 karena potongan menuju nol. Dalam praktik bisnis, ceil() dipakai untuk “jumlah kotak pengiriman minimal” (7,2 kotak berarti 8 kotak), floor() untuk kuota yang tidak boleh dilampaui, dan trunc() untuk memotong digit promo tanpa membulatkan. ceiling() hanyalah nama lain dari ceil() — keduanya dijalankan oleh fungsi yang sama.
Sisa Bagi, Pangkat, dan Akar #
Tiga fungsi penutup menutup kebutuhan aritmetika sehari-hari. mod() mengembalikan sisa pembagian dan punya padanan operator %; power() dan sqrt() menangani pangkat dan akar kuadrat:
-- mod() dan operator % menghasilkan sisa pembagian
SELECT mod(10, 3) AS sisa_fungsi,
10 % 3 AS sisa_operator,
mod(-10, 3) AS sisa_negatif;
-- power() menghitung pangkat, sqrt() menghitung akar kuadrat
SELECT power(2, 10) AS dua_pangkat_10,
sqrt(2) AS akar_dua;
-- pangkat negatif menghasilkan pecahan
SELECT power(10, -3) AS sepuluh_pangkat_minus_tiga;Output yang Dihasilkan:
sisa_fungsi | sisa_operator | sisa_negatif
-------------+---------------+--------------
1 | 1 | -1
(1 row)
dua_pangkat_10 | akar_dua
----------------+--------------------
1024 | 1.4142135623730951
(1 row)
sepuluh_pangkat_minus_tiga
----------------------------
0.001
(1 row)mod() dan % menghasilkan nilai yang sama untuk angka positif; untuk mod(-10, 3) hasilnya -1 — tanda hasil mengikuti tanda bilangan yang dibagi, bukan pembaginya. Fungsi mod() paling sering dipakai untuk pengelompokan berselang — misalnya WHERE id % 2 = 0 untuk mengambil baris genap, atau mod(id, 10) untuk membagi data uji ke sepuluh kelompok. Perhatikan juga bahwa power(10, -3) sah dan menghasilkan pecahan 0.001; pangkat negatif bukan error. Satu operasi yang benar-benar ditolak server adalah akar kuadrat dari bilangan negatif:
-- akar kuadrat dari bilangan negatif tidak ada di bilangan real
SELECT sqrt(-1) AS gagal;Output yang Dihasilkan:
psql:sqrt-negatif.sql:2: ERROR: cannot take square root of a negative numberError cannot take square root of a negative number muncul karena hasilnya bukan bilangan real — PostgreSQL tidak otomatis melompat ke bilangan imajiner. Bila data sensor Anda memang bisa bernilai negatif, saring dulu dengan abs() atau filter WHERE nilai >= 0 sebelum memanggil sqrt().
Dengan perangkat aritmetika ini, angka di kolom bisa dibulatkan, dipotong, dan dihitung sesuai aturan bisnis. Namun tanggal dan waktu punya kelas fungsi sendiri — mengambil komponen tahun, memotong ke awal bulan, menghitung usia — yang tidak bisa disentuh fungsi matematika biasa. Chapter berikutnya mengulas Fungsi Date dan Time .
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
abs-dan-sign.sql
Unduh berkas-- tabel mutasi kas sederhana untuk demo abs() dan sign()
CREATE TABLE mutasi (
id integer PRIMARY KEY,
keterangan text NOT NULL,
jumlah integer NOT NULL
);
INSERT INTO mutasi (id, keterangan, jumlah) VALUES
(1, 'penjualan tunai', 250000),
(2, 'pengeluaran belanja kopi', -175000),
(3, 'pengeluaran sewa kios', -500000),
(4, 'penjualan kredit', 125000);
SELECT keterangan, jumlah,
abs(jumlah) AS nilai_absolut,
sign(jumlah) AS arah
FROM mutasi
ORDER BY id;
-- sign() mengembalikan -1, 0, atau 1
SELECT sign(-7) AS negatif,
sign(0) AS nol,
sign(7.5) AS positif;
ceil-floor-trunc.sql
Unduh berkas-- ceil()/ceiling() membulatkan ke atas, floor() ke bawah
SELECT ceil(3.1) AS ke_atas,
ceiling(3.1) AS padanan_ceiling,
floor(3.9) AS ke_bawah,
ceil(-3.1) AS negatif_ke_atas,
floor(-3.1) AS negatif_ke_bawah;
-- trunc() memotong angka desimal tanpa membulatkan
SELECT trunc(3.9) AS potong_positif,
trunc(-3.9) AS potong_negatif,
trunc(123.456, 1) AS potong_satu_desimal,
trunc(123.456, -1) AS potong_puluhan;
mod-power-sqrt.sql
Unduh berkas-- mod() dan operator % menghasilkan sisa pembagian
SELECT mod(10, 3) AS sisa_fungsi,
10 % 3 AS sisa_operator,
mod(-10, 3) AS sisa_negatif;
-- power() menghitung pangkat, sqrt() menghitung akar kuadrat
SELECT power(2, 10) AS dua_pangkat_10,
sqrt(2) AS akar_dua;
-- pangkat negatif menghasilkan pecahan
SELECT power(10, -3) AS sepuluh_pangkat_minus_tiga;
pembulatan-float.sql
Unduh berkas-- pembulatan float mengikuti aturan "half to even" (dibulatkan ke angka genap)
SELECT round(2.5::float8) AS float_dua_setengah,
round(0.5::float8) AS float_nol_koma_lima,
round(3.5::float8) AS float_tiga_koma_lima;
-- NaN pada float dibandingkan seolah-olah lebih besar dari angka apa pun
SELECT 'NaN'::float8 > 100 AS nan_lebih_besar,
'NaN'::float8 = 'NaN'::float8 AS nan_sama_dengan_nan;
-- pembulatan NaN tetap menghasilkan NaN
SELECT round('NaN'::float8) AS bulatkan_nan;
pembulatan.sql
Unduh berkas-- round() pada numeric: nilai .5 dibulatkan menjauhi nol
SELECT round(2.5) AS positif_setengah,
round(-2.5) AS negatif_setengah,
round(2.4) AS di_bawah_setengah;
-- round() dengan jumlah angka desimal
SELECT round(123.456, 2) AS dua_desimal,
round(123.456, 0) AS tanpa_desimal;
-- round() dengan tempat negatif: pembulatan puluhan dan ratusan
SELECT round(1234, -2) AS ratusan_bawah,
round(1251, -2) AS ratusan_atas;
-- scale() melaporkan jumlah digit di belakang koma
SELECT scale(12.3450) AS skala_nilai;
round-float-error.sql
Unduh berkas-- round() dua argumen hanya tersedia untuk numeric, bukan double precision
SELECT round(3.14159::float8, 2) AS gagal;
sqrt-negatif.sql
Unduh berkas-- akar kuadrat dari bilangan negatif tidak ada di bilangan real
SELECT sqrt(-1) AS gagal;