CROSS JOIN SQL: Bikin Kombinasi Semua Baris buat Kalender Data
TL;DR
CROSS JOIN adalah perintah SQL yang memasangkan setiap baris dari tabel pertama dengan setiap baris dari tabel kedua, menghasilkan semua kombinasi yang mungkin. Jumlah barisnya adalah perkalian: tabel 30 baris di-CROSS JOIN dengan tabel 4 baris menghasilkan 120 baris. Beda dari JOIN lain, CROSS JOIN gak butuh klausa ON sama sekali. Kegunaan utamanya bikin kerangka lengkap tanggal kali cabang, supaya periode yang gak ada transaksinya tetap muncul di laporan sebagai nol, bukan hilang.
CROSS JOIN masangin setiap baris tabel A sama setiap baris tabel B. Semua kombinasi, tanpa syarat apa pun.
Tabel 3 baris ketemu tabel 4 baris? Hasilnya 12 baris. Gak ada klausa ON, gak ada yang dicocokin.
Kedengeran gak ada gunanya. Tapi ini satu-satunya cara bikin laporan harian yang tanggal kosongnya tetap muncul sebagai nol — bukan hilang begitu aja.
Apa itu CROSS JOIN di SQL?
CROSS JOIN adalah jenis JOIN yang menghasilkan semua kombinasi pasangan antara baris tabel kiri dan baris tabel kanan. Jumlah baris hasilnya sama dengan jumlah baris tabel kiri dikali jumlah baris tabel kanan.
Sintaksnya paling pendek di antara semua JOIN:
SELECT a.kolom, b.kolom
FROM tabel_a a
CROSS JOIN tabel_b b;
Gak ada ON. Emang gak butuh.
Istilah matematikanya cartesian product. Kalau kamu ketemu warning database yang nyebut istilah itu, artinya query kamu lagi ngasilin perkalian baris.
Gimana cara kerja CROSS JOIN?
Bikin dua tabel dulu:
CREATE TABLE cabang (
cabang_id INT PRIMARY KEY,
nama_cabang VARCHAR(50) NOT NULL
);
INSERT INTO cabang (cabang_id, nama_cabang) VALUES
(1, 'Bandung'),
(2, 'Cimahi'),
(3, 'Soreang');
Sekarang CROSS JOIN sama 3 tanggal:
SELECT c.nama_cabang, k.tanggal
FROM cabang c
CROSS JOIN (
SELECT DATE '2026-01-01' AS tanggal
UNION ALL SELECT DATE '2026-01-02'
UNION ALL SELECT DATE '2026-01-03'
) k
ORDER BY k.tanggal, c.nama_cabang;
Hasilnya 9 baris — 3 cabang dikali 3 tanggal:
| nama_cabang | tanggal |
|---|---|
| Bandung | 2026-01-01 |
| Cimahi | 2026-01-01 |
| Soreang | 2026-01-01 |
| Bandung | 2026-01-02 |
| Cimahi | 2026-01-02 |
| Soreang | 2026-01-02 |
| Bandung | 2026-01-03 |
| Cimahi | 2026-01-03 |
| Soreang | 2026-01-03 |
Kerangka kosong yang lengkap. Setiap cabang punya baris di setiap tanggal, terlepas dari ada transaksinya atau gak.
Kenapa kerangka kalender itu penting?
Ini masalah nyata yang aku temuin waktu ngerjain dashboard toko_berkah.
Datanya:
INSERT INTO penjualan (penjualan_id, cabang_id, tanggal, total) VALUES
(1, 1, '2026-01-01', 1250000),
(2, 2, '2026-01-01', 890000),
(3, 3, '2026-01-01', 640000),
(4, 1, '2026-01-02', 1420000),
(5, 2, '2026-01-02', 760000),
(6, 1, '2026-01-03', 980000),
(7, 2, '2026-01-03', 1100000);
Liat baik-baik. Soreang cuma jualan tanggal 1. Tanggal 2 dan 3 kosong — mungkin tutup, mungkin gak ada yang beli.
Query biasa:
SELECT c.nama_cabang, p.tanggal, SUM(p.total) AS omzet
FROM penjualan p
JOIN cabang c ON c.cabang_id = p.cabang_id
GROUP BY c.nama_cabang, p.tanggal;
Hasilnya cuma 7 baris. Soreang tanggal 2 dan 3 gak muncul sama sekali.
Masukin ini ke grafik tren, dan garis Soreang bakal langsung lompat dari tanggal 1 ke tanggal 4. Keliatan kayak jualannya lancar terus, padahal dua hari kosong melompong.
Solusinya: CROSS JOIN dulu, LEFT JOIN kemudian
SELECT
kerangka.tanggal,
kerangka.nama_cabang,
COALESCE(SUM(p.total), 0) AS omzet
FROM (
SELECT c.cabang_id, c.nama_cabang, d.tanggal
FROM cabang c
CROSS JOIN (
SELECT DATE '2026-01-01' AS tanggal
UNION ALL SELECT DATE '2026-01-02'
UNION ALL SELECT DATE '2026-01-03'
) d
) kerangka
LEFT JOIN penjualan p
ON p.cabang_id = kerangka.cabang_id
AND p.tanggal = kerangka.tanggal
GROUP BY kerangka.tanggal, kerangka.nama_cabang
ORDER BY kerangka.tanggal, kerangka.nama_cabang;
Sekarang keluar 9 baris. Soreang tanggal 2 dan 3 muncul dengan omzet 0.
| tanggal | nama_cabang | omzet |
|---|---|---|
| 2026-01-02 | Bandung | 1.420.000 |
| 2026-01-02 | Cimahi | 760.000 |
| 2026-01-02 | Soreang | 0 |
Angka nol itu informasi. Baris yang hilang bukan.
Urutan kerjanya: CROSS JOIN buat bikin rangkanya, LEFT JOIN buat nempelin data aslinya, COALESCE buat ngubah NULL jadi nol.
Gimana bikin deret tanggal tanpa ngetik satu-satu?
Ngetik UNION ALL 31 kali buat sebulan itu bikin capek. PostgreSQL punya jalan pintas:
SELECT c.nama_cabang, d.tanggal::DATE
FROM cabang c
CROSS JOIN generate_series(
DATE '2026-01-01',
DATE '2026-01-31',
INTERVAL '1 day'
) AS d(tanggal)
ORDER BY d.tanggal, c.nama_cabang;
3 cabang dikali 31 hari = 93 baris. Satu query, gak ada tabel bantu.
Detail generate_series ada di dokumentasi set returning functions PostgreSQL.
SQL Server dan MySQL 8 gak punya fungsi ini, tapi bisa pakai recursive CTE. Kalau males, bikin aja tabel kalender permanen sekali — isi tanggal 10 tahun ke depan, terus dipakai selamanya.
Bahaya: cross join yang gak sengaja
Ini penyebab query lambat nomor satu yang paling sering aku liat di kerjaan orang.
-- BAHAYA: gak ada kondisi penghubung
SELECT *
FROM penjualan p, cabang c;
Sintaks koma di FROM tanpa WHERE = CROSS JOIN. Database gak protes. Dia cuma nurut.
Kalau penjualan punya 50.000 baris dan cabang punya 3 baris, hasilnya 150.000 baris. Masih ketolong.
Tapi kalau dua tabelnya sama-sama 50.000 baris? 2,5 miliar baris. Query kamu bakal gantung, dan tim data bakal nanya siapa yang bikin server panas.
Cara ngindarinnya
- Selalu pakai sintaks
JOIN ... ONyang eksplisit, jangan koma di FROM - Tulis
CROSS JOINsecara eksplisit kalau emang itu yang kamu mau — biar orang lain tau itu disengaja - Cek jumlah baris pakai
SELECT COUNT(*)sebelum ngejalanin query berat - Kasih
LIMIT 100waktu lagi eksperimen
Kesalahan umum
1. CROSS JOIN tabel besar sama tabel besar
Kombinasinya bengkak eksponensial. Selalu CROSS JOIN tabel kecil (daftar cabang, daftar kategori, daftar tanggal), bukan tabel transaksi.
2. Naruh kondisi di WHERE, bukan di ON
-- Ini technically cross join + filter
FROM penjualan p, cabang c
WHERE p.cabang_id = c.cabang_id;
Hasilnya bener dan optimizer biasanya cukup pintar buat nanganin ini. Tapi bacanya susah, dan sekali kamu lupa nulis WHERE-nya, boom.
3. Lupa filter tanggal di kerangka
Bikin kerangka 5 tahun padahal cuma butuh 30 hari. Barisnya jadi 60x lebih banyak dari yang perlu, dan query-nya melambat tanpa alasan.
FAQ
Apa bedanya CROSS JOIN dan INNER JOIN?
INNER JOIN butuh klausa ON dan cuma nampilin baris yang cocok berdasarkan kondisi itu. CROSS JOIN gak punya ON sama sekali dan masangin semua baris tabel kiri dengan semua baris tabel kanan tanpa syarat. Jumlah barisnya jadi hasil perkalian. Kalau kamu nulis JOIN tapi lupa ON-nya, sebagian database diam-diam ngejalanin itu sebagai CROSS JOIN.
Kenapa hasil query saya jadi ribuan baris padahal datanya sedikit?
Kemungkinan besar kamu kena cross join gak sengaja. Ini kejadian kalau kamu nulis dua tabel di klausa FROM dipisah koma tanpa kondisi di WHERE, atau kalau kondisi JOIN-nya salah kolom. Tabel 500 baris ketemu tabel 300 baris bakal ngasilin 150 ribu baris.
CROSS JOIN buat apa sih di kerjaan nyata?
Yang paling sering: bikin kerangka laporan yang lengkap. Kamu CROSS JOIN daftar tanggal dengan daftar cabang atau produk, terus LEFT JOIN ke data transaksi. Hasilnya, tanggal yang gak ada penjualannya tetap muncul dengan nilai nol, bukan hilang dari laporan. Ini penting buat grafik tren biar garisnya gak lompat-lompat.
Apakah CROSS JOIN sama dengan cartesian product?
Iya, dua istilah ini merujuk ke hal yang sama. Cartesian product itu istilah matematikanya, CROSS JOIN itu sintaks SQL-nya. Keduanya berarti semua kombinasi yang mungkin dari dua himpunan. Kalau kamu baca error atau warning yang nyebut cartesian product, artinya query kamu lagi ngasilin perkalian baris.
Gimana cara bikin deret tanggal tanpa tabel kalender?
Di PostgreSQL pakai generate_series. Di SQL Server pakai recursive CTE. Di MySQL 8 juga bisa pakai recursive CTE. Kalau database kamu gak punya semuanya, bikin tabel kalender permanen sekali aja dan isi tanggalnya buat 10 tahun ke depan.
Penutup
Ringkasnya:
- CROSS JOIN = semua kombinasi, tanpa klausa ON. Jumlah baris = kiri dikali kanan.
- Kepakainya buat bikin kerangka kalender, biar periode kosong muncul sebagai nol.
- Cross join gak sengaja bisa ngeledakin query. Selalu tulis JOIN secara eksplisit.
Query di atas siap copy-paste. Coba jalanin, terus bandingin jumlah barisnya sebelum dan sesudah kerangka dipasang.
Latihan interaktifnya ada di halaman fungsi CROSS JOIN. Istilah cartesian product aku jelasin lengkap di glosarium cartesian product.
Lanjut baca: Cara JOIN Lebih dari Dua Tabel di SQL — biar barisnya gak kegandain waktu tabelnya nambah.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel Terkait
7 SQL Client Gratis Terbaik buat Analis Data (2026)
Nggak perlu bayar buat nulis query. Ini 7 SQL client gratis yang beneran dipakai analis, plus kelebihan dan kekurangannya masing-masing.
SQL Style Guide: Cara Nulis Query yang Enak Dibaca Tim
Sepuluh aturan SQL style guide yang bikin query kamu kebaca sama tim — dan sama kamu sendiri 6 bulan lagi. Lengkap dengan contoh sebelum-sesudah.
Take-Home Test SQL Data Analyst: Contoh Soal + Cara Ngerjainnya
Contoh soal take-home test SQL yang beneran dipakai perusahaan, dikerjain step by step — dari baca soal, nulis query, sampai nyusun insight yang bikin recruiter nengok.