Function hitung_diskon dari chapter sebelumnya menerima satu angka dan mengembalikan satu angka — jalur data yang sederhana namun terbatas. Kasir Toko Maju Jaya membutuhkan lebih dari itu: mengirim harga sekaligus jumlah item, menerima kembalian berupa rincian tiga kolom, menaikkan poin pelanggan yang nilainya lama ikut diperbarui, atau menghitung rata-rata dari daftar belanja yang panjangnya tidak pasti. PostgreSQL mengatur arah aliran data ini lewat mode parameter: IN, OUT, INOUT, dan VARIADIC. Chapter ini membedah keempatnya satu per satu, lalu melengkapi dengan nilai DEFAULT dan pemanggilan argumen bernama yang membuat function dengan banyak parameter tetap nyaman dipakai.

Peta lengkap mode parameter sebelum kita mulai:

ModeArah dataHasil keluar lewatCatatan
INmasukklausa RETURNSmode default; boleh tidak ditulis
OUTkeluarparameter itu sendirimengubah bentuk klausa RETURNS
INOUTmasuk lalu keluarparameter itu sendirinilai masuk diubah dan dikembalikan
VARIADICmasuk, jumlah bebasklausa RETURNSargumen dikumpulkan menjadi array

Parameter IN: Masukan Standar #

Tanpa kata kunci mode, parameter otomatis bersifat IN — murni masukan. Nama parameter (harga, jumlah) langsung menjadi variabel yang bisa dibaca di seluruh body function:

-- Parameter IN (mode default): harga dan jumlah masuk, satu nilai keluar
CREATE TABLE produk (
    id    integer PRIMARY KEY,
    nama  text NOT NULL,
    harga numeric(12,2) NOT NULL
);

INSERT INTO produk (id, nama, harga) VALUES
    (1, 'Kopi Gayo 250g',    85000.00),
    (2, 'Teh Melati 100g',   15000.00),
    (3, 'Gula Kristal 1kg',  16000.00);

CREATE FUNCTION hitung_bayar(harga numeric, jumlah integer)
RETURNS numeric
LANGUAGE plpgsql
AS $$
DECLARE
    v_total numeric;
BEGIN
    v_total := harga * jumlah;
    RETURN v_total;
END
$$;

SELECT hitung_bayar(15000, 3) AS teh_melati,
       hitung_bayar(85000, 2) AS kopi_gayo;

SELECT nama, hitung_bayar(harga, 1) AS harga_per_item
FROM produk
ORDER BY id;

Output yang Dihasilkan:

text
CREATE TABLE
INSERT 0 3
CREATE FUNCTION
 teh_melati | kopi_gayo 
------------+-----------
      45000 |    170000
(1 row)

       nama       | harga_per_item 
------------------+----------------
 Kopi Gayo 250g   |       85000.00
 Teh Melati 100g  |       15000.00
 Gula Kristal 1kg |       16000.00
(3 rows)

Perhatikan pemanggilan kedua: hitung_bayar(harga, 1) memakai kolom harga sebagai argumen pertama. Parameter IN tidak peduli asal nilainya — literal, variabel PL/pgSQL, atau kolom dari tabel produk semuanya sah, karena evaluasinya terjadi per baris oleh pemanggil SELECT.

Parameter OUT: Keluaran Lewat Parameter #

Mode OUT membalik arah: parameter dideklarasikan sebagai penampung hasil, dan function mengisinya lewat assignment. Konsekuensinya terasa pada klausa RETURNS:

-- Parameter OUT: hasil keluar lewat parameter, bukan RETURN
CREATE FUNCTION rincian_bayar(harga numeric, jumlah integer,
                              OUT total numeric,
                              OUT diskon numeric,
                              OUT total_bayar numeric)
LANGUAGE plpgsql
AS $$
BEGIN
    total       := harga * jumlah;
    diskon      := CASE WHEN total >= 100000 THEN total * 0.05 ELSE 0 END;
    total_bayar := total - diskon;
END
$$;

-- Bentuk 1: satu kolom berisi seluruh record
SELECT rincian_bayar(15000, 3);

-- Bentuk 2: FROM memecah record menjadi kolom-kolom
SELECT * FROM rincian_bayar(85000, 2);

Output yang Dihasilkan:

text
CREATE FUNCTION
  rincian_bayar  
-----------------
 (45000,0,45000)
(1 row)

 total  | diskon  | total_bayar 
--------+---------+-------------
 170000 | 8500.00 |   161500.00
(1 row)

Tiga hal penting dari contoh di atas:

  1. Klausa RETURNS tidak ada — kehadiran OUT total, OUT diskon, dan OUT total_bayar otomatis menjadikan tipe kembalian function sebuah record berisi tiga kolom tersebut. PostgreSQL menyusunnya untuk Anda.
  2. Tidak ada RETURN — function selesai saat END tercapai; nilai parameter OUT itulah hasilnya.
  3. Dua bentuk pemanggilan: SELECT rincian_bayar(...) menampilkan record utuh sebagai satu kolom (45000,0,45000), sedangkan SELECT * FROM rincian_bayar(...) memecahnya menjadi kolom total, diskon, total_bayar — bentuk yang biasanya dipakai aplikasi.

Parameter OUT juga membuat nama parameter jadi bagian dari antarmuka publik function: pemanggil melihatnya sebagai nama kolom hasil, bukan sekadar variabel internal.

Parameter INOUT: Dua Arah Sekaligus #

INOUT menggabungkan kedua arah: nilai masuk, boleh diubah, dan keluar kembali sebagai hasil. Program loyalitas Toko Maju Jaya memakainya untuk menaikkan poin:

-- Parameter INOUT: nilai masuk, diubah, lalu keluar lagi
CREATE FUNCTION naikkan_poin(poin_masuk inout integer, bonus integer)
LANGUAGE plpgsql
AS $$
BEGIN
    poin_masuk := poin_masuk + bonus;
END
$$;

SELECT naikkan_poin(450, 50)  AS siti_baru,
       naikkan_poin(1200, 75) AS budi_baru;

Output yang Dihasilkan:

text
CREATE FUNCTION
 siti_baru | budi_baru 
-----------+-----------
       500 |      1275
(1 row)

Pemanggilan naikkan_poin(450, 50) mengirim literal 450 masuk ke parameter poin_masuk, body menambahkannya dengan bonus, dan nilai baru 500 keluar sebagai hasil function. Pola ini tepat untuk operasi “nilai lama → nilai baru” seperti konversi satuan atau penyesuaian saldo — meski untuk kasus yang lebih kompleks, OUT tambahan biasanya lebih terbaca.

Parameter VARIADIC: Daftar Argumen Bebas #

Function bawaan seperti greatest() dan coalesce() menerima sejumlah argumen berapapun. Kuncinya adalah mode VARIADIC: seluruh argumen terakhir dikumpulkan menjadi satu array:

-- Parameter VARIADIC: jumlah argumen bebas, dikumpulkan menjadi array
CREATE FUNCTION rata_rata(VARIADIC angka numeric[])
RETURNS numeric
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN (SELECT round(avg(a), 2) FROM unnest(angka) AS a);
END
$$;

SELECT rata_rata(80, 90, 85) AS tiga_nilai,
       rata_rata(70, 80)     AS dua_nilai;

-- Array yang sudah ada dikirim utuh dengan label VARIADIC
SELECT rata_rata(VARIADIC ARRAY[90, 100, 60, 50]::numeric[]) AS empat_nilai;

Output yang Dihasilkan:

text
CREATE FUNCTION
 tiga_nilai | dua_nilai 
------------+-----------
      85.00 |     75.00
(1 row)

 empat_nilai 
-------------
       75.00
(1 row)

Body memperlakukan angka sebagai array numeric[] biasa — di sini unnest() melebarkannya menjadi baris sehingga agregat avg() dari chapter Agregasi Data langsung terpakai. Pemanggilan rata_rata(80, 90, 85) mengumpulkan tiga argumen; rata_rata(70, 80) cukup dua; dan bila array sudah tersedia, label VARIADIC pada pemanggilan mengirimnya utuh tanpa dikumpulkan ulang.

Nilai DEFAULT dan Argumen Bernama #

Parameter boleh punya nilai bawaan yang terpakai ketika pemanggil tidak mengirim argumen tersebut. Dipadukan dengan notasi bernama nama => nilai, function berparameter banyak menjadi jauh lebih nyaman:

-- Nilai DEFAULT + pemanggilan dengan argumen bernama
CREATE FUNCTION total_belanja(harga numeric,
                              jumlah integer DEFAULT 1,
                              persen_diskon numeric DEFAULT 0)
RETURNS numeric
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN round(harga * jumlah * (1 - persen_diskon / 100), 2);
END
$$;

-- Posisional penuh vs sebagian (argumen terakhir memakai DEFAULT)
SELECT total_belanja(50000)     AS tanpa_opsi,
       total_belanja(50000, 3)  AS pakai_jumlah,
       total_belanja(50000, 3, 10) AS lengkap;

-- Notasi bernama: urutan bebas, argumen jelas
SELECT total_belanja(harga => 50000, persen_diskon => 10) AS bernama;

-- Notasi campuran: posisi dulu, lalu bernama
SELECT total_belanja(50000, persen_diskon => 10) AS campuran;

Output yang Dihasilkan:

text
CREATE FUNCTION
 tanpa_opsi | pakai_jumlah |  lengkap  
------------+--------------+-----------
   50000.00 |    150000.00 | 135000.00
(1 row)

 bernama  
----------
 45000.00
(1 row)

 campuran 
----------
 45000.00
(1 row)

Tiga gaya pemanggilan di atas layak dihafal:

  1. Posisional: argumen diisi berurutan dari kiri; argumen buntut boleh dilewatkan dan memakai DEFAULT — total_belanja(50000) berarti jumlah 1, diskon 0.
  2. Bernama penuh: harga => 50000, persen_diskon => 10 — urutan bebas dan setiap nilai jelas milik parameter mana; jumlah tetap memakai DEFAULT.
  3. Campuran: argumen posisional didahulukan, sisanya bernama — total_belanja(50000, persen_diskon => 10).

Terakhir, ada keluarga tipe polimorfik yang bekerja diam-diam di balik banyak function bawaan. Parameter bertipe anyelement menyesuaikan diri dengan tipe argumen yang masuk: function greatest(anyelement, anyelement) menerima dua integer sekaligus dua text karena tipe kembalinya ikut “menunggu” tipe argumen. Anda bisa memakai pola yang sama pada function sendiri — misalnya CREATE FUNCTION tampil(x anyelement) RETURNS anyelement — dengan catatan pemanggil harus mengirim argumen bertipe jelas (literal polos 'Budi' perlu cast 'Budi'::text).

Kita kini bisa mengirim data masuk dan keluar function dengan leluasa. Pertanyaan berikutnya menyangkut bentuk hasil itu sendiri: satu angka, satu baris lengkap, atau seluruh tabel hasil query? Chapter Return Type menyusun pilihan-pilihan tersebut dari skalar hingga SETOF dan RETURNS TABLE.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

default-dan-nama.sql

Unduh berkas
sql
-- Nilai DEFAULT + pemanggilan dengan argumen bernama
CREATE FUNCTION total_belanja(harga numeric,
                              jumlah integer DEFAULT 1,
                              persen_diskon numeric DEFAULT 0)
RETURNS numeric
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN round(harga * jumlah * (1 - persen_diskon / 100), 2);
END
$$;

-- Posisional penuh vs sebagian (argumen terakhir memakai DEFAULT)
SELECT total_belanja(50000)     AS tanpa_opsi,
       total_belanja(50000, 3)  AS pakai_jumlah,
       total_belanja(50000, 3, 10) AS lengkap;

-- Notasi bernama: urutan bebas, argumen jelas
SELECT total_belanja(harga => 50000, persen_diskon => 10) AS bernama;

-- Notasi campuran: posisi dulu, lalu bernama
SELECT total_belanja(50000, persen_diskon => 10) AS campuran;

parameter-in.sql

Unduh berkas
sql
-- Parameter IN (mode default): harga dan jumlah masuk, satu nilai keluar
CREATE TABLE produk (
    id    integer PRIMARY KEY,
    nama  text NOT NULL,
    harga numeric(12,2) NOT NULL
);

INSERT INTO produk (id, nama, harga) VALUES
    (1, 'Kopi Gayo 250g',    85000.00),
    (2, 'Teh Melati 100g',   15000.00),
    (3, 'Gula Kristal 1kg',  16000.00);

CREATE FUNCTION hitung_bayar(harga numeric, jumlah integer)
RETURNS numeric
LANGUAGE plpgsql
AS $$
DECLARE
    v_total numeric;
BEGIN
    v_total := harga * jumlah;
    RETURN v_total;
END
$$;

SELECT hitung_bayar(15000, 3) AS teh_melati,
       hitung_bayar(85000, 2) AS kopi_gayo;

SELECT nama, hitung_bayar(harga, 1) AS harga_per_item
FROM produk
ORDER BY id;

parameter-inout.sql

Unduh berkas
sql
-- Parameter INOUT: nilai masuk, diubah, lalu keluar lagi
CREATE FUNCTION naikkan_poin(poin_masuk inout integer, bonus integer)
LANGUAGE plpgsql
AS $$
BEGIN
    poin_masuk := poin_masuk + bonus;
END
$$;

SELECT naikkan_poin(450, 50)  AS siti_baru,
       naikkan_poin(1200, 75) AS budi_baru;

parameter-out.sql

Unduh berkas
sql
-- Parameter OUT: hasil keluar lewat parameter, bukan RETURN
CREATE FUNCTION rincian_bayar(harga numeric, jumlah integer,
                              OUT total numeric,
                              OUT diskon numeric,
                              OUT total_bayar numeric)
LANGUAGE plpgsql
AS $$
BEGIN
    total       := harga * jumlah;
    diskon      := CASE WHEN total >= 100000 THEN total * 0.05 ELSE 0 END;
    total_bayar := total - diskon;
END
$$;

-- Bentuk 1: satu kolom berisi seluruh record
SELECT rincian_bayar(15000, 3);

-- Bentuk 2: FROM memecah record menjadi kolom-kolom
SELECT * FROM rincian_bayar(85000, 2);

parameter-variadic.sql

Unduh berkas
sql
-- Parameter VARIADIC: jumlah argumen bebas, dikumpulkan menjadi array
CREATE FUNCTION rata_rata(VARIADIC angka numeric[])
RETURNS numeric
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN (SELECT round(avg(a), 2) FROM unnest(angka) AS a);
END
$$;

SELECT rata_rata(80, 90, 85) AS tiga_nilai,
       rata_rata(70, 80)     AS dua_nilai;

-- Array yang sudah ada dikirim utuh dengan label VARIADIC
SELECT rata_rata(VARIADIC ARRAY[90, 100, 60, 50]::numeric[]) AS empat_nilai;