Col1, Col2, dan ColN dalam fungsi QUERY Google Sheets tidak merujuk pada kolom A, B, atau C di spreadsheet Anda - melainkan pada kolom ke-1, ke-2, dan ke-N dari range yang Anda masukkan. Jika range Anda adalah C1:F100, maka Col1 adalah kolom C, Col2 adalah D, Col3 adalah E, dan Col4 adalah F. Sesederhana itu konsepnya. Per April 2026, kesalahpahaman ini menjadi penyebab utama error #VALUE! dan parse error pada formula QUERY - lebih sering dari kesalahan sintaks lain mana pun.
Fungsi QUERY menggunakan Google Visualization API Query Language, yang mendukung 11 klausul berbeda: select, where, group by, pivot, order by, limit, offset, label, format, options, dan skipping. Notasi ColN adalah cara bahasa query tersebut mengidentifikasi kolom ketika nama header tidak tersedia - dan memahami mengapa jauh lebih penting daripada sekadar menghafal sintaksnya. Terlebih jika Anda bekerja dengan model keuangan multi-tab yang angkanya harus konsisten dari Laba Rugi ke Neraca ke Arus Kas.
Pemetaan Col1 Col2 ke Kolom Aktual di Sheet
Angka pada ColN mencerminkan posisi kolom dihitung dari kiri ke kanan, dimulai dari kolom pertama dalam range Anda. Berikut pemetaannya untuk berbagai range:
| Range | Col1 | Col2 | Col3 | Col4 | Col5 |
|---|---|---|---|---|---|
A:D | A | B | C | D | - |
B:E | B | C | D | E | - |
C:F | C | D | E | F | - |
LR!D:H | D | E | F | G | H |
Jadi =QUERY(C1:F100, "SELECT Col1, Col3 WHERE Col2 > 1000") memilih kolom C dan E, dengan filter pada kolom D. Jika Anda menulis SELECT C, E WHERE D > 1000, QUERY akan langsung melempar parse error - huruf kolom tidak berlaku dalam konteks ini.
Kebingungan ini semakin parah karena dokumentasi =QUERY di halaman bantuan Google Sheets terkadang menggunakan contoh dengan huruf kolom dan ColN secara bergantian, tanpa selalu menjelaskan kapan masing-masing sintaks berlaku.
Mengapa Sintaks Col1 Col2 Berubah Saat Kolom Disisipkan
Sisipkan satu kolom sebelum range Anda, dan seluruh referensi ColN di dalam query akan bergeser diam-diam. Formula tidak error - ia hanya mengembalikan data yang salah.
Bayangkan tab LR (Laba Rugi) dengan struktur berikut:
| A: Kuartal | B: Segmen | C: Pendapatan | D: HPP | E: Beban Ops | F: EBITDA | G: Margin |
|---|
Formula di tab Ringkasan Anda:
=QUERY(LR!A2:G50,
"SELECT Col1, Col3, Col6, Col7
WHERE Col2 = 'Konsolidasi'
ORDER BY Col1",
1)
Formula ini menarik Kuartal, Pendapatan, EBITDA, dan Margin - persis yang diinginkan. Lalu rekan kerja menyisipkan kolom "Penyusutan" antara HPP dan Beban Operasional untuk keperluan rekonsiliasi FCFF. Sekarang Col6 bukan lagi EBITDA, melainkan Beban Operasional. Formula tetap berjalan tanpa error - tetapi tab Ringkasan kini menampilkan angka yang salah di baris EBITDA.
Ini bukan sekadar bug biasa. Jika ini yang dipresentasikan ke LP atau direksi, angkanya terlihat benar tapi salah secara fundamental. Saran standar - "gunakan named ranges" atau "dokumentasikan di komentar" - tidak cukup untuk model yang diakses banyak orang dan harus dipertanggungjawabkan. Solusi yang benar-benar andal ada di seksi berikutnya.
Teknik Validasi untuk Model Keuangan Multi-Tab
Masalah pergeseran kolom memiliki dua solusi teknis yang bisa dikombinasikan: pemetaan kolom dinamis dan sel validasi kanari.
Pemetaan Kolom Dinamis dengan MATCH
Daripada hardcoding nomor kolom, gunakan MATCH untuk menemukan posisi kolom berdasarkan nama header, lalu gabungkan hasilnya ke dalam string query:
=LET(
h, LR!1:1,
c_kuartal, MATCH("Kuartal",h,0),
c_pendapatan, MATCH("Pendapatan",h,0),
c_ebitda, MATCH("EBITDA",h,0),
c_margin, MATCH("Margin",h,0),
c_segmen, MATCH("Segmen",h,0),
QUERY(LR!A2:Z50,
"SELECT Col"&c_kuartal&
", Col"&c_pendapatan&
", Col"&c_ebitda&
", Col"&c_margin&
" WHERE Col"&c_segmen&" = 'Konsolidasi'
ORDER BY Col"&c_kuartal,
0)
)
Sekarang jika rekan kerja menyisipkan kolom baru di mana saja, MATCH akan menemukan posisi "EBITDA" yang baru secara otomatis. Formula tetap menarik kolom yang benar. Range LR!A2:Z50 sengaja diperlebar hingga kolom Z agar QUERY selalu mencakup semua kolom, berapa pun yang disisipkan.
Sel Validasi Kanari
Tambahkan satu sel di atas area output QUERY - tidak perlu dicetak, cukup terlihat oleh tim - yang memverifikasi bahwa header masih di posisi yang diharapkan:
=IF(
AND(
INDEX(LR!1:1, MATCH("EBITDA", LR!1:1, 0)) = "EBITDA",
INDEX(Neraca!1:1, MATCH("Total Aset", Neraca!1:1, 0)) = "Total Aset"
),
"✓ Pemetaan kolom valid per " & TEXT(TODAY(), "dd/mm/yyyy"),
"âš PERIKSA HEADER - kolom mungkin bergeser"
)
Letakkan formula ini di pojok kiri atas tab Ringkasan dengan conditional formatting: hijau jika valid, merah jika ada peringatan. Ini bukan audit trail yang sempurna, tapi setidaknya ada alarm yang berbunyi sebelum angka yang salah sampai ke deck investor.
QUERY untuk Rekonsiliasi Antar Tab Keuangan
Berikut skenario konkret yang paling sering dihadapi analis keuangan: menarik dan merekonsiliasi angka dari beberapa tab sekaligus - Laba Rugi, Neraca, Arus Kas - ke dalam satu tab Ringkasan yang kohesif.
Misalkan model Anda memiliki tab LR (kolom A-H: Kuartal, Segmen, Pendapatan, HPP, Beban Ops, EBITDA, Margin, Catatan) dan tab ArusKas (kolom A-E: Kuartal, Segmen, CFO, CFI, CFF). Anda ingin menampilkan EBITDA dari LR berdampingan dengan CFO dari ArusKas untuk setiap kuartal, dengan filter hanya untuk segmen Konsolidasi:
=QUERY(
{LR!A2:H50, IFERROR(
VLOOKUP(LR!A2:A50, ArusKas!A:E, {3,4,5}, 0),
{"","",""})},
"SELECT Col1, Col6, Col7, Col9, Col10
WHERE Col2 = 'Konsolidasi'
ORDER BY Col1 DESC",
0
)
Range gabungan ini membentuk 11 kolom: 8 dari LR (Col1-Col8) dan 3 dari VLOOKUP ke ArusKas (Col9 = CFO, Col10 = CFI, Col11 = CFF). Anda memilih Col1 (Kuartal), Col6 (EBITDA), Col7 (Margin), Col9 (CFO), dan Col10 (CFI). Pembungkus IFERROR mengisi string kosong untuk kuartal yang datanya belum masuk di tab ArusKas - dalam model yang sedang disusun, kondisi ini wajar untuk 1-2 kuartal terakhir.
Untuk rekonsiliasi total yang lebih ketat - memverifikasi bahwa EBITDA di tab Ringkasan sama persis dengan total di tab LR - tambahkan sel pengecekan yang sepenuhnya independen dari ColN:
=IF(
SUMIF(LR!B2:B50, "Konsolidasi", LR!F2:F50)
= SUM(FILTER(Ringkasan!C2:C20, Ringkasan!A2:A20 <> "")),
"✓ EBITDA reconciled",
"âš Selisih: " & TEXT(
ABS(SUMIF(LR!B2:B50, "Konsolidasi", LR!F2:F50)
- SUM(FILTER(Ringkasan!C2:C20, Ringkasan!A2:A20 <> ""))),
"Rp #.##0")
)
Formula rekonsiliasi ini menggunakan referensi sel langsung - bukan ColN - sehingga tidak terpengaruh pergeseran kolom sama sekali. Kombinasikan ini dengan QUERY berbasis MATCH di atas, dan Anda punya layer keamanan berlapis: QUERY menarik data dengan pemetaan tahan geser, SUMIF memverifikasi totalnya sudah benar.
Menggunakan Kriteria Dinamis dalam QUERY dengan Sintaks Col
Klausul WHERE yang hardcoded cepat usang. Kuartal berganti, filter berubah, dan ada saja yang lupa memperbarui formula. Pola yang lebih tahan lama adalah menghubungkan kriteria filter ke sebuah sel:
=QUERY(LR!A2:H50,
"SELECT Col1, Col3, Col6, Col7
WHERE Col1 = date '"&TEXT(A2,"yyyy-mm-dd")&"'",
1)
TEXT(A2,"yyyy-mm-dd") memaksa tanggal ke dalam format yang tepat seperti yang diharapkan parser QUERY. Gunakan format lain dan Anda akan mendapat type mismatch. Ini bukan keanehan QUERY - memang sudah ditetapkan dalam spesifikasi Visualization API: literal tanggal harus mengikuti ISO 8601 (yyyy-MM-dd), sebagaimana tercantum dalam Query Language Reference Google.
Untuk filter rentang kuartal di model cashflow - skenario yang sering membutuhkan pembaruan manual setiap periode - pola ini lebih praktis:
=QUERY(ArusKas!A2:E50,
"SELECT Col1, Col3, Col4, Col5
WHERE Col1 >= date '"&TEXT(DATE(YEAR(B1),(QUARTER(B1)-1)*3+1,1),"yyyy-mm-dd")&"'
AND Col1 <= date '"&TEXT(EOMONTH(DATE(YEAR(B1),QUARTER(B1)*3,1),0),"yyyy-mm-dd")&"'
ORDER BY Col1",
1)
Di mana B1 berisi tanggal referensi kuartal. Ini menarik semua baris dalam satu kuartal berdasarkan satu sel yang bisa diubah - tidak perlu menyentuh formula setiap kali periode berganti.
Untuk filter teks:
=QUERY(LR!A2:H50,
"SELECT Col1, Col3, Col6
WHERE Col2 = '"&A1&"'",
1)
Hilangkan tanda kutip dalam string dan QUERY akan mencoba mengevaluasi nilai sel sebagai nama kolom. Angka 1 di akhir memberi tahu QUERY bahwa baris pertama adalah header - lewatkan argumen ini dan QUERY akan memperlakukan baris header Anda sebagai data.
Range Gabungan dan Penomoran Ulang ColN
Ketika Anda menggabungkan dua range dengan kurung kurawal - {LR!A:F, Neraca!G:I} - QUERY memperlakukan hasilnya sebagai satu tabel datar. Penomoran ColN dimulai ulang dari Col1 di kolom paling kiri dari range gabungan tersebut. Kolom G dari Neraca menjadi Col7 (atau posisi berapa pun ia jatuh dalam tabel gabungan), bukan Col1.
Pola lintas-tab yang umum untuk pelaporan konsolidasi ke manajemen:
=QUERY(
{LR!A2:F50, IFERROR(
VLOOKUP(LR!A2:A50, Neraca!A:D, {2,3,4}, 0),
{"","",""})},
"SELECT Col1, Col3, Col6, Col7, Col8, Col9
WHERE Col2 = 'Konsolidasi'
ORDER BY Col6 DESC",
0
)
Range gabungan ini memiliki total 9 kolom: 6 dari LR (Col1-Col6) dan 3 dari hasil VLOOKUP ke Neraca (Col7, Col8, Col9). Nilai 0 di akhir (jumlah baris header) disengaja karena kita membangun range secara programatik, bukan menarik langsung dari sheet yang memiliki header di baris 1.
Menangani Sintaks Col QUERY pada Dataset Besar
QUERY berjalan sepenuhnya di browser pada infrastruktur Google. Pada range dengan 4.800 baris dan beberapa kolom kalkulasi, hasilnya hampir instan. Pada 50.000 baris dengan beberapa cross-tab lookup bersarang, keterlambatan kalkulasi ulang mulai terasa - terutama jika sheet melakukan recalculate setiap kali ada perubahan. Google Sheets membatasi workbook di 10 juta sel total, dan batas waktu eksekusi 30 detik untuk QUERY berlaku jauh sebelum batas tersebut tercapai.
Beberapa hal yang benar-benar membantu pada skala besar:
Pertama, batasi scope range. LR!A:H (open-ended) memaksa QUERY memindai setiap baris hingga batas maksimum. LR!A2:H500 membatasinya. Untuk sebagian besar model keuangan operasional, range dengan batas atas adalah trade-off yang wajar antara risiko keakuratan dan kecepatan.
Kedua, urutan filter. Letakkan klausul WHERE yang paling selektif di urutan pertama. WHERE Col2 = 'Konsolidasi' AND Col1 >= date '2026-01-01' berjalan lebih cepat dibandingkan kondisi yang sama dengan urutan terbalik, jika Col2 mengeliminasi 90% baris.
Ketiga, pindahkan ke alat yang tidak melakukan kalkulasi ulang di sel browser. Jika Anda rutin melakukan query terhadap ekspor CSV 40.000 baris atau koneksi database langsung, integrasi DuckDB ModelMonkey menangani hal tersebut tanpa overhead recalculation - Anda cukup menulis SQL, hasilnya masuk ke sheet, tanpa perlu memikirkan pemetaan ColN sama sekali.
Sintaks Col QUERY vs. FILTER: Kapan Menggunakan Mana
Keduanya memfilter baris, tetapi menjawab pertanyaan yang berbeda.
| Situasi | Gunakan QUERY | Gunakan FILTER |
|---|---|---|
| Perlu seleksi kolom (SELECT) | Ya | Tidak |
| Perlu agregasi (GROUP BY, SUM) | Ya | Tidak |
| Filter dengan beberapa kondisi | Keduanya bisa | Keduanya bisa |
| Output dynamic array di Sheets terbaru | Keduanya bisa | Ya (lebih bersih) |
| Penanganan baris header | Bawaan (argumen ke-3) | Manual |
| Pengurutan kompleks (ORDER BY) | Ya | Tidak |
| Cross-tab dari range gabungan | Ya | Tidak praktis |
| Pemetaan kolom tahan geser (MATCH + LET) | Ya | Tidak relevan |
FILTER lebih sederhana untuk seleksi baris dasar. QUERY adalah pilihan tepat begitu Anda perlu mengubah bentuk atau mengagregasi data - memilih 3 dari 8 kolom, melakukan pivot kategori, atau mengelompokkan berdasarkan kuartal.
Seperti yang dicatat dalam dokumentasi fungsi Sheets Google, QUERY "menjalankan query Google Visualization API Query Language terhadap data" - artinya ini adalah query engine penuh, bukan sekadar lapisan filter.
FAQ
Apa perbedaan Col1 dan kolom A dalam QUERY Google Sheets?
Col1 merujuk pada kolom pertama dari range yang Anda masukkan ke QUERY, bukan kolom A di spreadsheet. Jika range Anda adalah D:G, maka Col1 adalah kolom D, Col2 adalah E, Col3 adalah F, dan Col4 adalah G. Huruf kolom seperti A, D, atau G tidak valid dalam sintaks QUERY dan akan menghasilkan parse error. Aturan sederhananya: hitung dari ujung kiri range Anda, bukan dari ujung kiri sheet.
Mengapa QUERY mengembalikan data yang salah setelah saya menyisipkan kolom baru?
Karena ColN dievaluasi berdasarkan posisi kolom dalam range pada saat query dieksekusi. Jika Anda menyisipkan kolom baru di dalam atau sebelum range, semua angka ColN bergeser - dan QUERY tidak memberikan peringatan apa pun. Solusinya adalah menggunakan MATCH untuk menemukan posisi kolom secara dinamis berdasarkan nama header, lalu menggabungkan hasilnya ke dalam string query dengan fungsi LET. Untuk model keuangan, tambahkan juga sel validasi kanari yang memverifikasi header masih di posisi yang diharapkan (lihat contoh di seksi teknik validasi di atas).
Bagaimana cara membuat filter dinamis dengan sintaks Col dalam QUERY?
Gabungkan nilai sel ke dalam string query menggunakan operator &. Untuk filter teks: "WHERE Col2 = '"&A1&"'". Untuk filter tanggal: "WHERE Col1 = date '"&TEXT(A2,"yyyy-mm-dd")&"'" - format ISO 8601 (yyyy-mm-dd) wajib digunakan, bukan format tanggal Indonesia pada umumnya. Untuk filter angka: "WHERE Col3 > "&B1 - tanpa tanda kutip karena angka tidak memerlukan delimiter string dalam sintaks QUERY.
Apakah sintaks ColN aman digunakan untuk model keuangan yang akan dipresentasikan ke investor?
Tidak secara langsung - ColN bersifat posisional, bukan nominal, sehingga rentan terhadap pergeseran diam-diam jika struktur sheet berubah. Untuk model yang akan diaudit atau dipresentasikan ke LP dan direksi, kombinasikan QUERY berbasis MATCH (pemetaan dinamis berdasarkan nama header) dengan formula rekonsiliasi SUMIF yang independen dari ColN. Layer ganda ini memastikan ada alarm yang terlihat jika pemetaan kolom bergeser - sebelum angka yang salah sampai ke laporan akhir.