Mengkueri tabel berpartisi
Dokumen ini menjelaskan beberapa pertimbangan khusus untuk mengkueri tabel berpartisi di BigQuery.
Untuk mengetahui informasi umum tentang cara menjalankan kueri di BigQuery, lihat Menjalankan kueri interaktif dan batch.
Ringkasan
Jika kueri menggunakan filter yang memenuhi syarat pada nilai kolom partisi, BigQuery dapat memindai partisi yang cocok dengan filter dan melewati partisi yang tersisa. Proses ini disebut pruning partisi.
Pruning partisi adalah mekanisme yang digunakan BigQuery untuk menghilangkan partisi yang tidak diperlukan dari pemindaian input. Partisi yang di-pruning tidak disertakan saat menghitung byte yang dipindai oleh kueri. Secara umum, pruning partisi membantu mengurangi biaya kueri.
Perilaku pruning bervariasi untuk berbagai jenis partisi, sehingga Anda dapat melihat perbedaan byte yang diproses saat mengkueri tabel yang dipartisi secara berbeda, tetapi ternyata identik. Untuk memperkirakan jumlah byte yang akan diproses, jalankan uji coba.
Mengkueri tabel berpartisi kolom unit waktu
Untuk melakukan pruning partisi saat Anda mengkueri tabel berpartisi berdasarkan kolom unit waktu, sertakan filter pada kolom partisi.
Pada contoh berikut, asumsikan bahwa dataset.table dipartisi pada
kolom transaction_date. Contoh kueri ini akan melakukan pruning tanggal sebelum 2016-01-01.
SELECT * FROM dataset.table WHERE transaction_date >= '2016-01-01'
Mengkueri tabel berpartisi berdasarkan waktu penyerapan
Tabel berpartisi berdasarkan waktu penyerapan
berisi kolom semu bernama _PARTITIONTIME, yang merupakan kolom
partisi. Nilai kolom adalah waktu penyerapan UTC untuk setiap baris,
yang dipotong sesuai batas partisi (seperti per jam atau harian), sebagai nilai
TIMESTAMP.
Misalnya, jika Anda menambahkan data pada 15 April 2021, 08:15:00 UTC, kolom
_PARTITIONTIME untuk baris tersebut berisi nilai berikut:
- Tabel berpartisi per jam:
TIMESTAMP("2021-04-15 08:00:00") - Tabel berpartisi harian:
TIMESTAMP("2021-04-15") - Tabel berpartisi bulanan:
TIMESTAMP("2021-04-01") - Tabel berpartisi tahunan:
TIMESTAMP("2021-01-01")
Jika perincian partisi adalah harian, tabel juga berisi kolom semu
bernama _PARTITIONDATE. Nilai ini sama dengan _PARTITIONTIME yang dipotong menjadi
nilai DATE.
Kedua nama kolom semu ini telah dipesan. Anda tidak dapat membuat kolom dengan nama mana pun di tabel Anda.
Untuk melakukan pruning partisi, filter salah satu kolom tersebut. Misalnya, kueri berikut hanya memindai partisi antara tanggal 1 Januari 2016 dan 2 Januari 2016:
SELECT column FROM dataset.table WHERE _PARTITIONTIME BETWEEN TIMESTAMP('2016-01-01') AND TIMESTAMP('2016-01-02')
Untuk memilih kolom semu _PARTITIONTIME, Anda harus menggunakan alias. Misalnya,
kueri berikut memilih _PARTITIONTIME dengan menetapkan alias pt ke
kolom semu:
SELECT _PARTITIONTIME AS pt, column FROM dataset.table
Untuk tabel berpartisi harian, Anda dapat memilih kolom semu _PARTITIONDATE
dengan cara yang sama:
SELECT _PARTITIONDATE AS pd, column FROM dataset.table
Kolom semu _PARTITIONTIME dan _PARTITIONDATE tidak ditampilkan oleh
pernyataan SELECT *. Anda harus memilihnya secara eksplisit:
SELECT _PARTITIONTIME AS pt, * FROM dataset.table
Menangani zona waktu dalam tabel berpartisi berdasarkan waktu penyerapan
Nilai _PARTITIONTIME didasarkan pada tanggal UTC saat kolom
diisi. Jika Anda ingin mengkueri data berdasarkan zona waktu selain UTC, pilih
salah satu opsi berikut:
- Sesuaikan untuk perbedaan zona waktu dalam kueri SQL Anda.
- Gunakan dekorator partisi untuk memuat data ke partisi waktu penyerapan tertentu, berdasarkan zona waktu yang berbeda dengan UTC.
Performa yang lebih baik dengan kolom semu
Untuk meningkatkan performa kueri, gunakan kolom semu _PARTITIONTIME saja
di sisi kiri perbandingan.
Misalnya, dua kueri berikut adalah setara. Bergantung pada ukuran
tabel, kueri kedua mungkin berperforma lebih baik karena menempatkan _PARTITIONTIME
sendiri di sisi kiri operator >. Kedua kueri memproses jumlah
data yang sama.
-- Might be slower. SELECT field1 FROM dataset.table1 WHERE TIMESTAMP_ADD(_PARTITIONTIME, INTERVAL 5 DAY) > TIMESTAMP("2016-04-15"); -- Often performs better. SELECT field1 FROM dataset.table1 WHERE _PARTITIONTIME > TIMESTAMP_SUB(TIMESTAMP('2016-04-15'), INTERVAL 5 DAY);
Untuk membatasi partisi yang dipindai dalam kueri, gunakan ekspresi konstan
dalam filter Anda. Kueri berikut membatasi partisi yang di-pruning berdasarkan
kondisi filter pertama dalam klausa WHERE. Namun, kondisi filter
kedua tidak membatasi partisi yang dipindai, karena menggunakan nilai tabel
yang dinamis.
SELECT column FROM dataset.table2 WHERE -- This filter condition limits the scanned partitions: _PARTITIONTIME BETWEEN TIMESTAMP('2017-01-01') AND TIMESTAMP('2017-03-01') -- This one doesn't, because it uses dynamic table values: AND _PARTITIONTIME = (SELECT MAX(timestamp) from dataset.table1)
Untuk membatasi partisi yang dipindai, jangan sertakan kolom lain dalam filter _PARTITIONTIME. Misalnya,
kueri berikut tidak membatasi partisi yang dipindai, karena field1
adalah kolom dalam tabel.
-- Scans all partitions of table2. No pruning. SELECT field1 FROM dataset.table2 WHERE _PARTITIONTIME + field1 = TIMESTAMP('2016-03-28');
Jika Anda sering membuat kueri untuk rentang waktu tertentu, pertimbangkan untuk membuat tampilan yang
memfilter kolom semu _PARTITIONTIME. Misalnya, pernyataan berikut
membuat tampilan yang hanya menyertakan data tujuh hari terakhir
dari tabel bernama dataset.partitioned_table:
-- This view provides pruning. CREATE VIEW dataset.past_week AS SELECT * FROM dataset.partitioned_table WHERE _PARTITIONTIME BETWEEN TIMESTAMP_TRUNC(TIMESTAMP_SUB(CURRENT_TIMESTAMP, INTERVAL 7 * 24 HOUR), DAY) AND TIMESTAMP_TRUNC(CURRENT_TIMESTAMP, DAY);
Untuk informasi tentang membuat tampilan, lihat Membuat tampilan.
Mengkueri tabel berpartisi berdasarkan rentang bilangan bulat
Untuk melakukan pruning partisi saat Anda mengkueri tabel berpartisi berdasarkan rentang bilangan bulat, sertakan filter pada kolom partisi bilangan bulat.
Pada contoh berikut, asumsikan bahwa dataset.table adalah tabel berpartisi
berdasarkan rentang bilangan bulat dengan spesifikasi partisi customer_id:0:100:10.
Contoh kueri memindai tiga partisi yang dimulai dengan 30, 40, dan 50.
SELECT * FROM dataset.table WHERE customer_id BETWEEN 30 AND 50 +-------------+-------+ | customer_id | value | +-------------+-------+ | 40 | 41 | | 45 | 46 | | 30 | 31 | | 35 | 36 | | 50 | 51 | +-------------+-------+
Pruning partisi tidak didukung untuk fungsi pada kolom berpartisi berdasarkan rentang bilangan bulat. Misalnya, kueri berikut memindai seluruh tabel.
SELECT * FROM dataset.table WHERE customer_id + 1 BETWEEN 30 AND 50
Mengkueri data di penyimpanan yang dioptimalkan untuk tulis
Partisi __UNPARTITIONED__ menyimpan data yang di-streaming ke
tabel berpartisi untuk sementara saat berada di
penyimpanan yang dioptimalkan untuk tulis.
Data yang di-streaming langsung ke partisi tertentu dari tabel berpartisi
tidak menggunakan partisi __UNPARTITIONED__. Sebaliknya, data akan di-streaming
langsung ke partisi.
Data di penyimpanan yang dioptimalkan untuk tulis memiliki nilai NULL dalam kolom _PARTITIONTIME
dan _PARTITIONDATE.
Untuk mengkueri data dalam partisi __UNPARTITIONED__, gunakan kolom semu _PARTITIONTIME
dengan nilai NULL. Contoh:
SELECT column FROM dataset.table WHERE _PARTITIONTIME IS NULL
Untuk informasi selengkapnya, lihat Streaming ke tabel berpartisi.
Praktik terbaik untuk pruning partisi
Bagian ini menjelaskan praktik terbaik untuk menulis kueri yang menggunakan penghapusan partisi untuk mengoptimalkan performa kueri dan mengurangi biaya.
Menggunakan ekspresi filter konstan
Untuk membatasi partisi yang dipindai dalam kueri, filter kolom partisi menggunakan ekspresi konstan, bukan ekspresi dinamis.
Kueri berikut melakukan pruning partisi:
SELECT t1.name, t1.quantity FROM table1 AS t1 WHERE t1.ts = CURRENT_TIMESTAMP()
Sebagai perbandingan, kueri berikut tidak melakukan pruning partisi, karena predikat,
WHERE t1.ts = (SELECT timestamp FROM table3 WHERE key = 2), bukan
ekspresi konstan. Kueri ini membandingkan kolom partisi dengan nilai dinamis, yang mencegah penghapusan partisi.
SELECT t1.name, t1.quantity FROM table1 AS t1 WHERE t1.ts = (SELECT timestamp FROM table3 WHERE key = 2)
Selain itu, kueri dengan predikat berikut tidak melakukan pruning partisi
karena memerlukan komputasi berdasarkan kolom tabel kedua yang tidak konstan
ts2 atau duration:
WHERE ts >= ts2 WHERE ts < CURRENT_TIMESTAMP() - duration
Mengisolasi kolom partisi atau menggunakan fungsi yang didukung
Untuk memangkas partisi, kondisi filter harus disusun agar BigQuery dapat menentukan partisi mana yang akan dipindai tanpa membaca data tabel. Untuk melakukannya, pisahkan kolom partisi di satu sisi operator perbandingan, atau bungkus kolom hanya dalam fungsi bawaan yang didukung. Anda dapat menggunakan uji coba untuk memverifikasi apakah pemangkasan partisi didukung pada kueri tertentu.
Fungsi bawaan berikut pada kolom partisi mendukung penghapusan partisi, jika argumen tambahannya konstan:
DATE_ADD,DATE_DIFF,DATE_SUB,DATE_TRUNC,EXTRACTdenganYEARbagian,DATETIME_DIFF,TIMESTAMP_ADD,TIMESTAMP_DIFF,TIMESTAMP_SUB,TIMESTAMP_TRUNC,EXTRACTdengan bagianDATEatauYEAR,FORMAT_TIMESTAMPdengan penentu format berikut:%F,%Y-%m-%d, dan%Y%m%d.
Fungsi lain dan operasi matematika yang kompleks akan memerlukan pemindaian tabel penuh.
Contoh
Kueri berikut menunjukkan contoh predikat yang mendukung penghapusan partisi.
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE datehour = '2025-03-30 12:00:00';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE datehour >= '2025-03-30' AND datehour < TIMESTAMP_ADD('2025-03-30', INTERVAL 1 DAY);
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE DATE(datehour) = '2025-03-30';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE EXTRACT(DATE FROM datehour) = '2025-03-30';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE CAST(datehour AS DATE) = '2025-03-30';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE datehour >= '2025-01-01' AND datehour < '2025-02-01';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE TIMESTAMP_TRUNC(datehour, MONTH) >= '2025-04-01' AND TIMESTAMP_TRUNC(datehour, MONTH) < '2025-07-01';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE TIMESTAMP_DIFF(datehour, '2025-01-01', DAY) < 1;
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE TIMESTAMP_ADD(datehour, INTERVAL 1 DAY) < '2025-01-03';
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE TIMESTAMP_SUB(datehour, INTERVAL 1 DAY) < '2025-01-01';
Kueri berikut melewati semua partisi karena predikat tidak cocok dengan baris mana pun.
SELECT COUNT(*) FROM `bigquery-public-data.wikipedia.pageviews_2025` WHERE EXTRACT(YEAR FROM datehour) = 1900;
Kueri berikut memilih hari pertama setiap bulan dalam tabel, dan mendukung penghapusan partisi.
SELECT COUNT(*) FROM bigquery-public-data.wikipedia.pageviews_2025
WHERE DATE(datehour) IN UNNEST(GENERATE_DATE_ARRAY(
DATE_TRUNC(CURRENT_DATE(), YEAR),
DATE(DATE_TRUNC(CURRENT_DATE(), YEAR) + INTERVAL 1 YEAR - INTERVAL 1 DAY),
INTERVAL 1 MONTH
))Kueri dengan predikat berikut tidak memangkas partisi karena memanipulasi kolom partisi dengan fungsi yang tidak didukung:
WHERE FORMAT_DATE('%Y-%m-%d %H', ts) = '2025-03-28 20'; WHERE EXTRACT(MONTH FROM ts) = 3 AND EXTRACT(HOUR FROM ts) = 20
Demikian pula, kueri dengan predikat berikut tidak memangkas partisi karena memanipulasi kolom partisi dengan operasi aritmatika:
WHERE ts + INTERVAL 1 DAY > CURRENT_TIMESTAMP()
Untuk mengaktifkan pruning partisi, Anda harus menulis ulang ekspresi dengan mengisolasi
kolom partisi ts dari fungsi atau operasi aritmatika yang tidak didukung. Untuk rentang waktu, gunakan >= dan < untuk mengambil rentang yang tepat. Untuk
aritmatika, pindahkan operasi ke sisi perbandingan yang lain.
Kueri berikut memungkinkan penghapusan partisi dengan mengisolasi kolom
partisi ts untuk rentang waktu:
WHERE ts >= '2025-03-28 20:00:00' AND ts < '2025-03-28 21:00:00'
Kueri berikut memungkinkan penghapusan partisi dengan mengisolasi kolom partisi dari operasi aritmatika:
WHERE ts > CURRENT_TIMESTAMP() - INTERVAL 1 DAY
Memfilter beberapa kolom
Predikat pada kolom partisi dalam kueri tidak membatasi hal lain yang dapat Anda filter. Anda dapat menyertakan predikat pada kolom lain dalam klausa WHERE yang sama, dan pemangkasan partisi akan tetap terjadi selama kondisi yang mengevaluasi kolom partisi mengikuti praktik terbaik. Perhatikan bahwa AND penting dalam contoh berikut. Jika AND diubah menjadi OR, pemangkasan partisi tidak akan berfungsi, karena meskipun partisi tidak cocok dengan predikat pada kolom partisi, partisi tersebut tetap tidak dapat dipangkas. Data dalam partisi ini dengan meter_id = 1234 tetap memenuhi syarat untuk kueri.
Perhatikan bahwa predikat tidak perlu ditulis dalam urutan tertentu. Dalam contoh kueri berikut, dengan asumsi partisi pada kolom ts, pemangkasan partisi tetap terjadi terlepas dari penempatan predikat.
WHERE meter_id = 1234 AND ts >= '2025-03-28 20:00:00' AND ts < '2025-03-28 21:00:00'
Mewajibkan filter partisi dalam kueri
Saat membuat tabel berpartisi, Anda dapat mewajibkan penggunaan filter
predikat dengan mengaktifkan opsi Wajibkan filter partisi. Jika opsi ini
diterapkan, upaya untuk mengkueri tabel berpartisi tanpa menentukan klausa
WHERE akan menghasilkan error berikut:
Cannot query over table 'project_id.dataset.table' without a filter that can be
used for partition elimination.
Persyaratan ini juga berlaku untuk kueri pada tampilan dan tampilan terwujud yang mereferensikan tabel yang dipartisi.
Setidaknya harus ada satu predikat yang hanya merujuk ke kolom partisi agar
filter dianggap memenuhi syarat untuk penghapusan partisi. Untuk
tabel yang dipartisi pada kolom partition_id dengan kolom tambahan f
dalam skemanya, kedua klausa WHERE berikut memenuhi persyaratan:
WHERE partition_id = "20221231" WHERE partition_id = "20221231" AND f = "20221130"
Namun, berikut ini tidak cukup, dan akan menghasilkan error:
WHERE partition_id = "20221231" OR f = "20221130"
Untuk tabel berpartisi berdasarkan waktu penyerapan, gunakan kolom semu _PARTITIONTIME atau
_PARTITIONDATE.
Untuk informasi selengkapnya tentang menambahkan opsi Wajibkan filter partisi saat membuat tabel berpartisi, lihat Membuat tabel berpartisi. Anda juga dapat memperbarui setelan ini di tabel yang sudah ada.
Langkah berikutnya
- Untuk ringkasan tabel berpartisi, lihat Pengantar tabel berpartisi.
- Untuk mempelajari lebih lanjut cara membuat tabel berpartisi, lihat Membuat tabel berpartisi.