Jenis numerik

Memilih antara integer, bigint, numeric, dan floating point, termasuk aturan penyimpanan nilai uang.

Daftar isi

Topik ini membahas jenis data numerik PostgreSQL, yaitu keluarga integer, numeric presisi tetap, dan floating point, beserta aturan memilihnya. Pembahasan memakai tabel kecil di luar dataset utama agar setiap perilaku terlihat terisolasi. Setelah topik ini, pilihan tipe untuk pengenal, jumlah benda, dan nilai uang memiliki dasar yang jelas.

Keluarga integer#

Tiga jenis integer tersedia menurut lebar penyimpanannya, yaitu smallint dua byte, integer empat byte, dan bigint delapan byte. Lebar yang lebih besar menampung rentang nilai yang lebih jauh, dan rentang ini ditegakkan server, sehingga nilai yang melampaunya ditolak alih-alih dipotong.

bash
# rentang dan penolakan nilai di luar rentang integer
psql -U postgres -d perpustakaan <<'SQL'
CREATE TABLE angka (biasa INTEGER);
INSERT INTO angka VALUES (2147483647);
INSERT INTO angka VALUES (2147483648);
SELECT * FROM angka;
SQL
text Keluaran
CREATE TABLE
INSERT 0 1
ERROR:  integer out of range
   biasa
------------
 2147483647
(1 row)

Keluaran di atas menunjukkan pola yang penting. Pernyataan pertama berhasil, pernyataan kedua ditolak oleh server, dan karena psql menjalankan heredoc baris demi baris, pernyataan SELECT tetap dieksekusi setelahnya dengan baris yang berhasil dimasukkan. Pada aplikasi nyata, error seperti ini berhenti di lapisan aplikasi, dan pilihan antara integer serta bigint dilakukan saat desain, bukan saat error muncul.

bash
# hapus tabel latihan numerik
PGOPTIONS='-c client_min_messages=warning' psql -q -U postgres -d perpustakaan -c "DROP TABLE IF EXISTS angka;"

Kebiasaan yang aman memakai integer untuk hitungan yang pasti kecil, dan bigint untuk pengenal maupun jumlah yang berpotensi tumbuh, misalnya baris log atau transaksi. bigint sama-sama berperforma baik pada perangkat modern, sehingga keraguan searah ke bigint lebih murah daripada migrasi tipe.

numeric untuk presisi eksak#

Jenis numeric menyimpan bilangan desimal secara eksak dengan batas presisi dan skala yang dideklarasikan, misalnya NUMERIC(8,2) berarti sampai delapan digit dengan dua digit di belakang koma. Pembandingnya adalah floating point ganda, yaitu float8 atau double precision, yang cepat tetapi menyimpan nilai biner terdekat dan bukan desimal eksak.

bash
# perbandingan presisi numeric dan floating point
psql -U postgres -d perpustakaan -c "SELECT 0.1::numeric + 0.2::numeric AS eksak, 0.1::float8 + 0.2::float8 AS aproksimasi;"
text Keluaran
 eksak |     aproksimasi
-------+---------------------
   0.3 | 0.30000000000000004
(1 row)

Tidak ada kesalahan pada kolom kedua, melainkan sifat representasi biner floating point yang tidak mampu menampung 0.1 secara persis. Selama perhitungan ilmiah memakai float8 dengan sengaja dan secara terbuka, perilaku ini normal. Yang tidak normal adalah membiarkan nilai uang melewatinya.

Nilai uang memerlukan numeric#

Nilai uang membutuhkan penjumlahan dan pembulatan yang dapat diaudit hingga sen terakhir, sehingga tipenya numeric, bukan floating point. Presisi dan skalanya dideklarasikan sesuai kebutuhan, dan NUMERIC(12,2) menampung nilai hingga sepuluh digit rupiah dengan dua digit sen atau seribu rupiah sesuai konvensi yang dipilih.

bash
# pembulatan numeric terhadap dua tempat desimal
psql -U postgres -d perpustakaan -c "SELECT round(1234.567::numeric, 2) AS dibulatkan, (1234.567::float8)::numeric(12,2) AS lewat_float;"
text Keluaran
 dibulatkan | lewat_float
------------+-------------
    1234.57 |     1234.57
(1 row)

Contoh kecil ini belum memperlihatkan kerusakan, tetapi akumulasi ribuan penjumlahan floating point dapat menggeser total akhir, dan selisih itu kemudian tidak cocok dengan bukti transaksi. Pola yang benar menyimpan uang sebagai numeric sejak awal, dan memakai float hanya pada metrik statistik yang memang toleran terhadap aproksimasi.

Pembagian integer dan konversi#

Operasi pembagian mengikuti tipe operannya. Pembagian dua integer menghasilkan integer dengan sisa dibuang, sehingga 5 / 2 bernilai 2, bukan 2.5. Untuk memperoleh pecahan, salah satu operan dikonversi ke numeric atau ditulis dengan tanda koma.

bash
# pembagian integer versus pembagian numeric
psql -U postgres -d perpustakaan -c "SELECT 5 / 2 AS pembagian_int, 5.0 / 2 AS pembagian_desimal, 5::numeric / 2 AS pembagian_konversi;"
text Keluaran
 pembagian_int | pembagian_desimal  | pembagian_konversi
---------------+--------------------+--------------------
             2 | 2.5000000000000000 | 2.5000000000000000
(1 row)

Tabel berikut merangkum pilihan tipe numerik yang paling sering dipakai.

TipeUkuranCakupanKapan dipakai
smallint2 byte-32768 sampai 32767nilai kecil yang pasti, misalnya kode status
integer4 bytesekitar 2,1 miliarpengenal dan hitungan dalam aplikasi biasa
bigint8 bytesekitar 9,2 kuintiliunpengenal dan hitungan yang berpotensi tumbuh
numeric(p,s)bervariasidesimal eksak sesuai deklarasinilai uang dan angka yang wajib eksak
float88 byteaproksimasi binerperhitungan ilmiah yang toleran aproksimasi

Kesalahan umum#

Kesalahan pertama adalah menyimpan uang sebagai float8 demi kenyamanan, yang menimbulkan selisih audit saat total dijumlahkan. Kesalahan kedua adalah memakai integer untuk pengenal baris pada tabel yang tumbuh cepat, karena melampaui 2,1 miliar baris memaksa migrasi tipe pada tabel hidup. Kesalahan ketiga adalah lupa bahwa pembagian dua integer membuang sisanya, sehingga persentase seperti bagian / total * 100 bernilai nol ketika bagian lebih kecil daripada total.

Kesalahan keempat adalah mengira numeric selalu lebih lambat secara berarti. Untuk beban aplikasi biasa, selisihnya jauh lebih kecil daripada biaya memperbaiki data uang yang sudah tergeser, sehingga pilihan eksak tetap menjadi bawaan yang disarankan untuk nilai finansial.

Ringkasan#

  • smallint, integer, dan bigint menampung rentang bilangan bulat sesuai lebar byte-nya, dan nilai di luar rentang ditolak server.
  • numeric menyimpan desimal secara eksak dan menjadi tipe wajib untuk nilai uang, sedangkan float8 bersifat aproksimasi biner.
  • Pembagian dua integer membuang sisa, sehingga konversi ke numeric diperlukan untuk hasil pecahan.
  • Keraguan memilih antara integer dan bigint searah ke bigint, karena migrasi tipe pada tabel hidup jauh lebih mahal.

Referensi#