Berapa transaksi masuk hari ini? Berapa penjual yang benar-benar aktif? Berapa produk berbeda yang terjual bulan ini? Semua pertanyaan itu bermuara pada satu pekerjaan: menghitung. Fungsi count adalah agregat yang paling sering dipakai sekaligus paling sering menjebak — karena di balik tulisannya yang seragam tersembunyi empat wujud berbeda: count(*), count(kolom), count(1), dan count(DISTINCT kolom), dan hanya wujud pertama yang sungguh-sungguh menghitung baris. Chapter Fungsi Agregat di section sebelumnya sudah mencicipi selisih count(*) lawan count(diskon) secara kilat; chapter ini membedah keempat wujudnya sampai tuntas, memakai satu tabel latihan yang akan menemani kita sepanjang section ini.

Tabel Latihan: penjualan #

Seluruh chapter di section ini memakai domain yang sama: tabel penjualan milik Toko Maju Jaya berisi 12 transaksi sepanjang Januari sampai Februari 2026, dari empat penjual — Budi, Siti, Andi, dan Dewi. Dua kolom sengaja dibiarkan boleh NULL karena itulah bahan percobaan utama kita:

  • kategori — NULL berarti transaksi belum dikategorikan (terjadi pada 2 baris).
  • diskon — NULL berarti transaksi tanpa diskon (terjadi pada 6 baris).
sql buat-tabel.sql
-- 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:

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

count(*): menghitung baris #

count(*) menjawab pertanyaan “ada berapa baris” — titik. NULL di kolom mana pun tidak berpengaruh karena yang dihitung adalah keberadaan barisnya, bukan isinya. Wujud count(1) juga termasuk di sini: argumen count pada dasarnya bisa berupa ekspresi apa pun, dan karena literal 1 tidak pernah bernilai NULL, count(1) menghitung setiap baris — hasilnya selalu sama dengan count(*):

-- Empat wujud COUNT pada tabel yang sama.
-- count(*): menghitung baris, termasuk baris yang kolomnya NULL.
SELECT count(*) AS semua_baris
FROM penjualan;

-- count(1): ekspresi 1 tidak pernah NULL, jadi hasilnya sama dengan count(*).
SELECT count(1) AS dengan_angka_satu
FROM penjualan;

-- count(kolom): hanya baris yang kolomnya berisi nilai (bukan NULL) yang dihitung.
SELECT count(*)           AS semua_baris,
       count(kategori)    AS kategori_terisi,
       count(diskon)      AS diskon_terisi,
       count(*) - count(diskon) AS diskon_kosong
FROM penjualan;

Output yang Dihasilkan:

text
 semua_baris 
-------------
          12
(1 row)

 dengan_angka_satu 
-------------------
                12
(1 row)

 semua_baris | kategori_terisi | diskon_terisi | diskon_kosong 
-------------+-----------------+---------------+---------------
          12 |              10 |             6 |             6
(1 row)

Query ketiga adalah momen pentingnya: count(*) melaporkan 12 transaksi, count(kategori) hanya 10 karena dua transaksi belum dikategorikan, dan count(diskon) hanya 6 karena enam transaksi berjalan tanpa diskon. Selisihnya bahkan bisa dipakai sebagai kalkulator: count(*) - count(diskon) menghasilkan 6 tanpa perlu menulis WHERE diskon IS NULL.

count(kolom): hanya nilai yang terisi #

Inilah aturan yang membedakan count dari empat agregat lainnya. count(kolom) menelusuri kolom itu dan hanya menghitung baris yang berisi nilai — NULL dilewati sepenuhnya. Gabungan kedua perilaku di atas menghasilkan peta lengkap berikut:

BentukYang dihitungHasil pada tabel latihan
count(*)Semua baris, apa pun isinya12
count(1)Semua baris (literal 1 selalu “terisi”)12
count(kategori)Baris dengan kategori terisi10
count(diskon)Baris dengan diskon terisi6

count(DISTINCT): menghitung nilai berbeda #

Menambahkan kata DISTINCT di depan nama kolom menyusutkan hitungan lagi: hanya nilai yang berbeda dari kolom itu yang dihitung, dan NULL tetap tidak pernah dihitung:

-- count(DISTINCT ...): menghitung nilai BERBEDA saja.
-- Produk unik: 5 nama berbeda dari 12 baris.
SELECT count(DISTINCT produk) AS produk_unik
FROM penjualan;

-- NULL tidak pernah dihitung oleh count(kolom), termasuk versi DISTINCT-nya:
-- kategori terisi di 10 baris, tapi nilainya yang berbeda hanya 3 (Kopi, Teh, Gula).
SELECT count(kategori)          AS kategori_terisi,
       count(DISTINCT kategori) AS kategori_berbeda
FROM penjualan;

-- Bandingkan tiga pertanyaan: total baris, penjual berbeda, produk berbeda.
SELECT count(*)                AS transaksi,
       count(DISTINCT penjual) AS penjual_aktif,
       count(DISTINCT produk)  AS produk_terjual
FROM penjualan;

Output yang Dihasilkan:

text
 produk_unik 
-------------
           5
(1 row)

 kategori_terisi | kategori_berbeda 
-----------------+------------------
              10 |                3
(1 row)

 transaksi | penjual_aktif | produk_terjual 
-----------+---------------+----------------
        12 |             4 |              5
(1 row)

Dua belas baris hanya mengandung 5 produk berbeda dan 4 penjual aktif. Perhatikan pasangan count(kategori) berbanding count(DISTINCT kategori): 10 lawan 3 — sepuluh baris terisi, tetapi nilainya mampat menjadi tiga kategori saja. NULL tidak pernah masuk hitungan DISTINCT, jadi dua baris tanpa kategori tidak menambah apa pun; bila Anda ingin menghitung kelompok NULL ini sebagai satu nilai, bungkus dulu dengan coalesce(kategori, '-').

count pada himpunan kosong: 0, bukan NULL #

Apa yang terjadi bila filter tidak cocok dengan satu baris pun? Untuk count, jawabannya tetap angka — nol:

-- count() pada himpunan kosong tetap mengembalikan angka: 0, bukan NULL.
-- Tidak ada transaksi pada Maret 2026 di tabel latihan.
SELECT count(*) AS transaksi_maret
FROM penjualan
WHERE tanggal >= DATE '2026-03-01';

-- Bandingkan dengan agregat lain pada filter yang sama persis:
-- sum() mengembalikan NULL karena tidak ada nilai untuk dijumlahkan.
SELECT count(*) AS transaksi,
       sum(jumlah) AS total_item
FROM penjualan
WHERE tanggal >= DATE '2026-03-01';

Output yang Dihasilkan:

text
 transaksi_maret 
-----------------
               0
(1 row)

 transaksi | total_item 
-----------+------------
         0 |           
(1 row)

Query kedua memperlihatkan dua watak berbeda dalam satu hasil: count(*) mengembalikan 0, sedangkan sum(jumlah) mengembalikan NULL. Perilaku sum pada himpunan kosong — beserta pola coalesce untuk mengatasinya — dibahas penuh di chapter SUM . Untuk count, Anda tidak pernah perlu khawatir: hasilnya selalu bilangan bulat, tidak pernah NULL.

Menghitung per kelompok dan dengan syarat #

Dua arah pengembangan yang paling sering dipakai di dunia nyata: memecah hitungan per kelompok, dan menghitung hanya baris yang memenuhi syarat:

-- Preview 1: menghitung per kelompok dengan GROUP BY (dibahas tuntas
-- di chapter GROUP BY). Setiap penjual mendapat baris hitungannya sendiri.
SELECT penjual,
       count(*) AS transaksi,
       count(diskon) AS dengan_diskon
FROM penjualan
GROUP BY penjual
ORDER BY penjual;

-- Preview 2: count dengan klausa FILTER — menghitung hanya baris
-- yang lolos syarat, tanpa memisah query (dibahas tuntas di chapter
-- FILTER Clause).
SELECT count(*) AS semua_transaksi,
       count(*) FILTER (WHERE kategori = 'Kopi') AS transaksi_kopi,
       count(*) FILTER (WHERE harga >= 85000)     AS transaksi_mahal
FROM penjualan;

Output yang Dihasilkan:

text
 penjual | transaksi | dengan_diskon 
---------+-----------+---------------
 Andi    |         3 |             1
 Budi    |         4 |             3
 Dewi    |         2 |             1
 Siti    |         3 |             1
(4 rows)

 semua_transaksi | transaksi_kopi | transaksi_mahal 
-----------------+----------------+-----------------
              12 |              4 |               5
(1 row)

Query pertama membelah 12 transaksi menjadi empat baris — satu per penjual — dan kolom count(diskon) langsung memperlihatkan bahwa hanya Budi yang rajin memberi diskon (3 dari 4 transaksinya). Query kedua tetap menghasilkan satu baris tetapi dengan tiga hitungan berbeda: 12 transaksi total, 4 di antaranya kopi, 5 di antaranya berharga 85000 ke atas. Mekanisme GROUP BY dibedah tuntas di chapter GROUP BY , dan klausa FILTER di chapter FILTER Clause .

Referensi #

Berkas Sumber

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

buat-tabel.sql

Unduh berkas
sql
-- 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;

count-distinct.sql

Unduh berkas
sql
-- count(DISTINCT ...): menghitung nilai BERBEDA saja.
-- Produk unik: 5 nama berbeda dari 12 baris.
SELECT count(DISTINCT produk) AS produk_unik
FROM penjualan;

-- NULL tidak pernah dihitung oleh count(kolom), termasuk versi DISTINCT-nya:
-- kategori terisi di 10 baris, tapi nilainya yang berbeda hanya 3 (Kopi, Teh, Gula).
SELECT count(kategori)          AS kategori_terisi,
       count(DISTINCT kategori) AS kategori_berbeda
FROM penjualan;

-- Bandingkan tiga pertanyaan: total baris, penjual berbeda, produk berbeda.
SELECT count(*)                AS transaksi,
       count(DISTINCT penjual) AS penjual_aktif,
       count(DISTINCT produk)  AS produk_terjual
FROM penjualan;

count-himpunan-kosong.sql

Unduh berkas
sql
-- count() pada himpunan kosong tetap mengembalikan angka: 0, bukan NULL.
-- Tidak ada transaksi pada Maret 2026 di tabel latihan.
SELECT count(*) AS transaksi_maret
FROM penjualan
WHERE tanggal >= DATE '2026-03-01';

-- Bandingkan dengan agregat lain pada filter yang sama persis:
-- sum() mengembalikan NULL karena tidak ada nilai untuk dijumlahkan.
SELECT count(*) AS transaksi,
       sum(jumlah) AS total_item
FROM penjualan
WHERE tanggal >= DATE '2026-03-01';

count-per-kelompok.sql

Unduh berkas
sql
-- Preview 1: menghitung per kelompok dengan GROUP BY (dibahas tuntas
-- di chapter GROUP BY). Setiap penjual mendapat baris hitungannya sendiri.
SELECT penjual,
       count(*) AS transaksi,
       count(diskon) AS dengan_diskon
FROM penjualan
GROUP BY penjual
ORDER BY penjual;

-- Preview 2: count dengan klausa FILTER — menghitung hanya baris
-- yang lolos syarat, tanpa memisah query (dibahas tuntas di chapter
-- FILTER Clause).
SELECT count(*) AS semua_transaksi,
       count(*) FILTER (WHERE kategori = 'Kopi') AS transaksi_kopi,
       count(*) FILTER (WHERE harga >= 85000)     AS transaksi_mahal
FROM penjualan;

hitung-baris.sql

Unduh berkas
sql
-- Empat wujud COUNT pada tabel yang sama.
-- count(*): menghitung baris, termasuk baris yang kolomnya NULL.
SELECT count(*) AS semua_baris
FROM penjualan;

-- count(1): ekspresi 1 tidak pernah NULL, jadi hasilnya sama dengan count(*).
SELECT count(1) AS dengan_angka_satu
FROM penjualan;

-- count(kolom): hanya baris yang kolomnya berisi nilai (bukan NULL) yang dihitung.
SELECT count(*)           AS semua_baris,
       count(kategori)    AS kategori_terisi,
       count(diskon)      AS diskon_terisi,
       count(*) - count(diskon) AS diskon_kosong
FROM penjualan;